Tim Deschryver

Parameterized selectors

Published | Modified

"How can I select an entity from the store by its id?"

This question popped up several times lately and in this post, I’ll provide some suggestions to create a selector that works with parameters, so let’s not waste any time, and let’s get started!

EDIT 2018–08–17: NgRx 6.1

The following will be more or less a copy of the NgRx docs.

As of NgRx 6.1 selectors also accepts an extra props argument. Which means you can now define a selector as the following:

Inside your component you can use the selector as usual but you can define the props value:

Keep in mind that a selector is memoized, meaning it will cache the result from the last parameters. If you would re-use the above selector but with another props value, it would clear the previous cache. If both selectors would be used at the same time, as in the example below, the selector would be invoked with every props value, thus the memoization would be more or less useless.

Every time the counter value changes in the above example, the selector would be invoked 2 times, one time for counter2, the other time for counter4. To allow memoization we can use a factory function to create the selector.

And in our component we can invoke the factory function fromRoot.getCount() to create a new selector instance for each counter, allowing each instance to have its own cache.

Static parameter

If the parameter doesn’t change over time we can use a factory function selectCustomer which has a parameter id and returns a selector. Making it possible to use the id parameter in our selector to retrieve the customer, resulting in the following:

We can then call selectCustomer in the component and pass it an id:

Dynamic parameter

If the id parameter is dynamic we can create a selector that instead of returning customers, returns a function which expects a parameter id. The selector becomes:

And in our component:

For this example, I’m also going to show the HTML, because it’s maybe not that straight forward. Because the selector returns a function now, we can call it like a normal function in our HTML:

To overcome this syntax inside the HTML we can also solve this with the RxJS map operator, as mentioned by Juliano Pável.

Filtering data in the component (within the RxJS stream)

We can also hook into the RxJS stream and map/filter our selector result by using one or more RxJS operators. In the snippet below, we select all the customers from the store and retrieve the current customer from it.

This approach does have a few drawbacks:

Using Global Store State

While the above examples work, for me retrieving data from the store like this feels a bit dirty and I consider it a bad practice in most cases. In my opinion, it's better to persist this "filter" state in the store, in our example it would mean that the id parameter would exist somewhere in the store.

This has the benefit that we're going to be fully reactive. When the filter's state changes, the selectors will be re-invoked (with the updated state), and our component will receive the new filtered state.

To implement this we must define actions that we can dispatch whenever a filter (the customer's id) changes. For example when the user clicks on a customer or when the user navigates to a customer's page, we dispatch an action. These actions are updating the selectedCustomerId property in the store state.

We also have to create a selector selectSelectedCustomerId to select the selectedCustomerId from the state. Because selectors are composable we can use both selectors to get the selected customer in the selectSelectedCustomer selector. The selectors looks like this:

In the component, we can consume the selector the ‘usual way’ without having to worry about the customer's id.

In the future when we have another filter, or when we add an action that changes the filter we don't have to worry about displaying the correct data in the component. It will just work because this selector is entirely driven my the Store's state.

NgRx Router Store

Another possibility would be to use @ngrx/router-store, this module connects the route with the store. In other words, all the route information will be available in the store thus also in the selectors. After installing the ngrx/router-store module and having it imported in our AppModule, we’ll first have to create a selector selectRouteParameters to retrieve the route parameters (customerId in our case). Thereafter we can use the created selector in selectCurrentCustomer to select the current customer. This means that when a user clicks on a link or navigates directly to /customers/47, she or he would see the customer’s details of customer 47. The selector looks roughly the same:

And the component remains the same (except for the selector’s name):

That’s a wrap

In my opinion the code we ended up with looks cleaner than the code we started with. I hope this was useful if you were looking for a way to parameterize your selector.

Some extra resources

Please consider supporting me if have you enjoyed this post and found it useful:

Buy Me A Coffee PayPal logo
Support the blog Share on Twitter Discuss on Twitter Edit on GitHub