2

Is there a way to pass parameters to ngrx select functions?

Following is my use case:

I am maintaining list of comments in store.

I wrote one component to represent one comment. So, one CommentComponent knows the id of the component object

Each comment will have properties like likedBy, reportedBy, ...etc In UI, I am showing all the components using *ngFor

Now I want my CommentComponent to subscribe only to one comment object by using id of the component.

Right now I am subscribing to all the comments in a top level component, and passing each comment to the CommentCompoent as an input.

Current approach is not clean because angular change detection strategy (even if I use onPush) has to render DOM for all the comments even though only one comment changes.

Appreciate any suggestions where I can pass comment id to a selector function so that each CommentComponent can subscribe to only one comment.

Thanks in advance, Sudhakar

user2960993
  • 711
  • 2
  • 8
  • 11
  • 2
    Are you subscribing to your comment inside your comment component? If yes, you should rather subscribe to the array in parent component and pass each of them using @input. You should keep your comment component as dumb as possible ;) – maxime1992 Sep 08 '17 at 08:13
  • @Maxime currently I am doing the same as you mentioned, but angular is taking too much time to render whenever any one comment changes – user2960993 Sep 08 '17 at 13:04
  • This is weird. Open source repo? Can you reproduce on Plunkr or Stackblitz? – maxime1992 Sep 08 '17 at 13:08
  • I know this is old, but are you using track by? This should prevent screen from re-rendering comments components. Also did you find an answer to this I have a different use case I want it for. – jgerstle Apr 09 '18 at 13:33
  • What is track by? – user2960993 Apr 11 '18 at 05:21
  • Another vote for using a `trackBy`, this will ensure no (expensive) DOM writes even when your data mutates. https://angular.io/api/common/NgForOf#ngForTrackBy – bc1105 May 08 '18 at 15:27

3 Answers3

7

Yes, you can pass parameters to the ngrx selector and you can do that in this way

from the component side

this.store.select(getComments(user_id));

from the selector side

export const getComments = (user_id) => createSelector(
  getData,
  (store) => store.comments 
);
Jaya Krishna
  • 313
  • 4
  • 14
  • Does this prevent memoization (which I think is a major purpose of createSelector) by returning a new selector each time? And is this recommended in official documentation? – Marcus Jun 20 '18 at 11:41
  • @Marcus, every time you use `getComments` it will create a new selector's instance. Thus memoization will still apply here. You can read [NgRx: Parameterized selectors](https://blog.angularindepth.com/ngrx-parameterized-selector-e3f610529f8) for more info. – timdeschryver Jan 06 '19 at 12:32
  • Doesn't seem to work well when you want to override such selector: https://ngrx.io/guide/store/testing#using-mock-selectors – CularBytes Oct 07 '20 at 12:42
  • Mocking it can be done bby just mocking the other selectors. But when you unit test the selector itself, there is no way to provide the initial state https://ngrx.io/guide/store/testing#testing-selectors – CularBytes Feb 10 '22 at 11:33
1

As of NgRx 6.1, you can use a selector with props.

export const getCount = () =>   
  createSelector(     
    (state, props) => state.counter[props.id],     
    (counter, props) => counter * props.multiply
);

For more info and different ways, check out my article NgRx: Parameterized selectors

timdeschryver
  • 14,415
  • 1
  • 19
  • 32
0

@CularBytes I've been working through a similar issue with using this approach with testing for awhile now and think i've found a way to make it work.

With the selector of

export const getItemsByProperty = (property: string, value: any) => createSelector(getAllItems, (items: ItemObj[]) => items.filter((item) => item[property] == value));

and where

export const getAllItems = createSelector(getState, (state) => selectAll(state.items));

in my components unit test file i override the selector for the getItemsByProperty's underlying selector call, getAllItems, with data and then expect the filtered data in my tests. If what you want to return changes, then just update the result of getAllItems.

A Hutch
  • 101
  • 1
  • 4