Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass an Angular Signal value to an NgRx selector

With regard to NgRx v16 new with Angular Signals, given:

export const selectCustomer = (id: string) =>
  createSelector(selectCustomers, (customers) => customers[id]);

(common expression; taken from Tim Deschryver's blog-post on how to do this with NgRx, up to v15)

Question 1, how should this be done when I have a Signal<string> for id?

FYI, currently I keep the selector as-was and use Angular's toSignal/toObservable, which seems like a bad detour:

import { toSignal, toObservable } from '@angular/core/rxjs-interop'
…
let id: Signal<string>;
…
const customer$ = toSignal(toObservable(id).pipe(
  switchMap(id => store.select(selectCustomer(id)))
));

Also, toObservable requires an injection context, so there are limitations where it can be used in the code.


And regarding memoization like

import * as _ from "lodash";
…
export const selectCustomer = _.memoize((id: string) =>
  createSelector(selectCustomers, (customers) => customers[id])
);

Question 2, will such manual memoization then become unnecessary?

like image 413
Christian Fuchs Avatar asked Sep 05 '25 03:09

Christian Fuchs


2 Answers

With set withComponentInputBinding in appConfig I got it to work with using computed.

// books.component.ts

export class BookDetailsComponent {
  private store = inject(Store);

  id = input<string>();
  book = computed(() => this.store.selectSignal(selectBook(this.id()))());
}

// app.config.ts

export const appConfig: ApplicationConfig = {
  providers: [
provideRouter(
  routes,
  withComponentInputBinding() // route id parameter => to id input signal
),
provideStore(reducers, { metaReducers, runtimeChecks }),
provideEffects(effects),
provideHttpClient(),
  ],
};
like image 84
zedL Avatar answered Sep 07 '25 22:09

zedL


Question 1:

i think you can do it as before (Tim's Versin) and don't need the interop package at all if you use ngrx v16. just call the signal to select from the store:

customer$ = store.select(selectCustomer(id())

and if you want to get a signal back from the store directly just use

customer = store.selectSignal(selectCustomer(id())

if you can't use ngrx v16 then you need to use the interop package like you did.

Question 2

Why do you want memoization for the selectorFactory? In your example all "source selectors" are already memoized. So the projector function just get's called if one of source selectors emit a new value and respect the id you used to return the calculated value. The created selector is also memoized so i don't know why you should memoize the factory function.

like image 40
SteffenLm Avatar answered Sep 07 '25 22:09

SteffenLm