Custom Select() with parameter
Asked Answered
T

4

8

UPDATE

As of @NGXS v3.1, they finally introduced arguments into @Selector().

https://www.ngxs.io/concepts/select#lazy-selectors

Examples from the DOCS

First, you define the @Selector "pandas"

@State<string[]>({
  name: 'animals',
  defaults: []
})

@Injectable()
export class ZooState {
  @Selector()
  static pandas(state: string[]) {
    return (type: string) => {
      return state.filter(s => s.indexOf('panda') > -1).filter(s => s.indexOf(type) > -1);
    };
  }
}

Then you just call it in your '.ts' file

import { Store } from '@ngxs/store';
import { map } from 'rxjs/operators';

@Component({ ... })
export class ZooComponent {
  babyPandas$: Observable<string[]>;

  constructor(private store: Store) {
    this.babyPandas$ = this.store
      .select(ZooState.pandas)
      .pipe(map(filterFn => filterFn('baby')));
  }
}

* From Old Post *

I am trying to create a custom @Select () to be able to drill down a particular tree and return the values dynamically. Getting either undefined or it's not making it (executing)

user.component.ts

const location = 'new york'
@Select(state => UserState.getUserLocationSlots(state, location)) slots$;

user.state.ts

@Selector() 
static getUserLocationSlots(state: UserStateModel, location: any) {
  console.log(state);
  console.log(location); // <-- expecting 'new york', but getting undefined
}
Tamaratamarack answered 3/5, 2018 at 19:9 Comment(0)
M
4

I don't think it is possible to pass parameter to @Selector() decorated functions in ngxs v2. It would be nice though.

A ticket exist for this feature request.

Also, I think you are not using @Selector() correctly. I should be something like (hence, cannot pass parameters):

@Select(UserState.getUserLocationSlots) slots$

Refer to the docs.

Note: I am not an expert in ngxs...this is just based on what I understand now.

Mixup answered 4/5, 2018 at 10:26 Comment(2)
Frankly, anything help. Thanks. Going to try the following @Select(state => state.user['location'].slots) user$;Tamaratamarack
Yup, didn't work. I'll keep looking and post a reply if I can ever get it to workTamaratamarack
S
14

You can achieve this by using crateSelector function from @ngxs/store

In your .state.ts file:

static getLocationSlots(location: string) {
    return createSelector([UserState], (state: string[) => {
        // logic for filtering your data
        // eg.: state.filter(element => element == location)
    })
}

In your .component.ts file:

@Select(UserState.getLocationSlots('new york')) slots$: Observable<any>

You can also check here for more details

Shutter answered 22/4, 2019 at 14:38 Comment(1)
What about if the @Select() is in an injectable Service and from the component you want to send the 'new york' as param? Is there a nice way for this?Monadnock
M
4

I don't think it is possible to pass parameter to @Selector() decorated functions in ngxs v2. It would be nice though.

A ticket exist for this feature request.

Also, I think you are not using @Selector() correctly. I should be something like (hence, cannot pass parameters):

@Select(UserState.getUserLocationSlots) slots$

Refer to the docs.

Note: I am not an expert in ngxs...this is just based on what I understand now.

Mixup answered 4/5, 2018 at 10:26 Comment(2)
Frankly, anything help. Thanks. Going to try the following @Select(state => state.user['location'].slots) user$;Tamaratamarack
Yup, didn't work. I'll keep looking and post a reply if I can ever get it to workTamaratamarack
N
4

To pass parameters you can have the select return a function, it isn't elegant, however it works.

For example the select statement would look like:

  @Selector()
  static getItemByIdFn(state: { [id: number]: Entity }) {
    return (id: number) => {
      return state[id];
    };
  }

then in the component:

this.store.select(MyState.getItemByIdFn)
  .pipe(map(mapByIdFn) => mayByIdFn(1)) // using the returned function
  .subscribe(...);

Note the map, which is where you pass your id to the returned function. Here you can place whatever parameters you would like.

Hope this helps :)!

Nomen answered 9/7, 2018 at 7:28 Comment(0)
D
3

This is achievable in NGXS v2 & v3. Copied from my comment in the discussion on dynamic selectors here

We can achieve this at the moment using a pattern often used for redux selectors...

The @Selector decorator can be written so that it returns a function with the desired parameter. This enables the desired dynamic selector arguments as well as late resolution of the selected state. For Example:

@State<UserStateModel>( ... ) 
export class UserState {   
  @Selector()  
  getFilteredUsersFn(userStateModel: UserStateModel) {
    return (filter: string) => 
      userStateModel.users.filter((user) => user.indexOf(filter) >= 0);
  }
} 

And then the component would contain:

@Component({...}) 
export class AppComponent {  
  @Select(UserState.getFilteredUsersFn) 
  filteredUsersFn$: Observable<(filter: string) => User[]>;

  get currentFilteredUsers$() {
    return this.filteredUsersFn$
      .pipe(map(filterFn => filterFn('myFilter')));   
  } 
} 
Dinahdinan answered 21/5, 2018 at 7:1 Comment(1)
Thanks, Mark, giving this a shot.Tamaratamarack

© 2022 - 2024 — McMap. All rights reserved.