Hongbo Miao
Hongbo Miao

Reputation: 49804

How to know which state value changes in ngrx?

When I use ngrx, if I want to transfer a value to its child component, I am doing like this:

// parent component

<product1-component [product]="(model$ | async)?.product1"></product1-component>
<product2-component [product]="(model$ | async)?.product2"></product2-component>

this.model$ = Observable
  .combineLatest(
    this._store.select('products')
  )
  .let(ProductsModel());

Right now I want to use the value product1 and product2 inside of the parent component itself. I am doing like this right now (is there any better way?):

this.model$.subscribe((model: any) => {
  if (!model) return;
  // Right now here no matter which value changes, it will run.
  // However what I want is:
  // if `product1` changes, do something
  // if `product2` changes, do something
});

How can I know which state value changes? Thanks

Upvotes: 0

Views: 2906

Answers (1)

Onlyann
Onlyann

Reputation: 335

The select function of the store returns an Observable. As such, you are then free to use any of the operators available in Rxjs to achieve what you want.

To answer your question what you can do is :

const nonNullModel$ = this.model$.filter(x => !!x);

this.product1$ = nonNullModel$.map(x => x.product1);
this.product2$ = nonNullModel$.map(x => x.product2);

Keep in mind that every time your products state slice changes, both product1$ and product2$ will push a new value. If you are only interested when the product1 or product2 really changes, you can use the distinctUntilChanged operator:

this.product1$ = nonNullModel$.map(x => x.product1).distinctUntilChanged();

Because this is pretty much what select does for you, you could instead write:

this.product1$ = this_store.select(x => x.products && x.products.product1);
this.product2$ = this_store.select(x => x.products && x.products.product2);

You can now use each stream directly in your template using the async pipe the same way you pass the value down to the child component.

<product1-component [product]="product1$ | async"></product1-component>
<product2-component [product]="product2$ | async"></product2-component>

JSON representation of my product 1: {{product1$ | async | json}}

If you want to do something in the parent component class:

this.sub1 = this.product1$.subcribe(p => // Do something with product 1);
this.sub2 = this.product2$.subcribe(p => // Do something with product 2);

Note that when you subscribe explicitly (as opposed to using async pipe) to an observable, you should take care of unsubscribing when you component gets destroyed.

ngOnDestroy() {
    this.sub1 && this.sub1.unsubcribe();
    this.sub2 && this.sub2.unsubcribe();
}

Upvotes: 2

Related Questions