Search code examples
angulartypescriptrxjsngrx-store

Is this the correct way to access state variables within a class when using @ngrx/store?


I have a service that needs to make decisions based on the value of a variable stored in the AppState. Here is a greatly simplified version of my service:

import { Injectable } from '@angular/core';
import { Store, select } from '@ngrx/store';

interface AppState {
  foo: boolean;
}

@Injectable()
export class FooProvider {

  private isFoo: boolean;

  constructor(private store: Store<AppState>) {
    // is it foo?
    store.pipe(select('foo')).subscribe((foo: boolean) => {
      this.isFoo = foo;
    });
  }

  isItFoo = (): string => this.isFoo ? "it's foo!" : 'nope, not foo';
}

Question: Is this the "right" way to access and use a variable stored in the AppState within a class in an app using @ngrx/store?

In a Component I believe I could use the foo variable much more simply with the async pipe, like so:

import { Component } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs';

interface AppState {
  foo: boolean;
}

@Component(
  selector: 'foo-component',
  template: `<p>{{ ( foo$ | async ) ? "it's foo!" : "nope, not foo" }}</p>`
)
export class FooComponent {

  private foo$: Observable<boolean>;

  constructor(private store: Store<AppState>) {
    this.foo$ = store.pipe(select('foo'));
  }
}

Is there a better/easier/more correct way to access state variables within a class?


Solution

  • As explained in this issue, the capability of getting current value was intentionally removed in ngrx because it was misused, and now this is achievable only via a hack:

    function getValue(o) {
      let value;
      o.take(1).subcribe(v => {
        value = v;
      });
      return value;
    }
    

    The purpose of observables is to provide a stream of data. Store value can be changed, and if a service that uses it isn't reinstantiated, it will continue to use old value. So it's expected that once there is a stream, it's transformed and combined with other streams and subscribed only where it's consumed (most times this will be a view, where it can be used with async pipe).

    In this case it will be:

    isItFoo$ = store.pipe(
      select('foo'),
      map((foo: boolean) => foo ? "it's foo!" : 'nope, not foo')
    );