Search code examples
angularreduxngrxrxjs-observablesangular-ngrx-data

Angular + Ngrx : best practice to select a value in component and in function


I have pretty much the same question as in ngrx get value in function , but I'm wondering if the answer (see comment in that topic) is still the best practice nowadays.

My situation is:

  1. I want to get 2 values from the store to show in my component --> I use a selector and the async pipe
  2. I need to pass those same values to an Angular Material dialog, to work with them in the dialogcomponent.

I have a working solution: I use subscribe functions in ngOnInit() and set a local variable with the value from the Observable. So I don't need to use the async pipe anymore and I can easily pass the value to some functions... This one sounds like the best option, but everywhere I look for answers I see "avoid using subscriptions".

So I'm wondering:

  • is there a better way of handling this scenario?
  • Or is this "the ngrx/rxjs way of handling this scenario"?
  • Or do I use the selector Observable and async pipe to show the value in the component AND subscribe to the Observable to create a local variable to pass to my function (this seems a bit redundant...)
  • Or ...?

My problem is that I want a consistent approach of handling ngrx values, but now it looks like I need to use 2 approaches depending on the scenario:

  • Async pipe for showing values in component (preferred approach according to the community)
  • Subscription when using values in local functions (community advises against the use of subscriptions "unless necessary")

(To my surprise: it's hard to find a clear answer on the internet ...)


Solution

  • Right, you need to avoid subscriptions. Use async pipe with as, ideally you'll have 0 subscriptions in your code.

    In the component class we create a stream that selects data from store

    export class MyComponent implements OnDestroy {
        public readonly data$: Observable<{
            user: User,
            items: Array<Item>,
        }>;
    
        constructor(protected readonly store: Store<StoreState>) {
            this.data$ = combineLatest([
                this.store.select(currentUser),
                this.store.select(someItems),
            ]).pipe(
                map(([user, items]) => ({
                    user,
                    items,
                })),
            );
        }
    
        public submit(user: User, item: Item): void {
            // some logic here
        }
    }
    

    and then in its template we have a wrapper that resolves it.

    <ng-container *ngIf="data$ | async as data">
        <div>header</div>
        <button *ngFor="let item of data.items" (click)="submit(data.user, item)">{{data.user.name}} {{item.name}}</button>
        <div>footer</div>
    </ng-container>