angularngrxngrx-component-store

ngrx selector wait for condition until emitting a value


I want to create a ngrx selector selectResult$ which only emits values once a certain input observable has a certain value.

Lets take this example:

export interface AppState {
  data: string[];
  settings: string[];
  loading: boolean; 
}

@Injectable()
export class AppStore extends ComponentStore<AppState> {

   selectLoading$ = this.select((state) => state.loading);
   selectSettings$ = this.select((state) => state.settings);
   selectData$ = this.select((state) => state.data);
   
   selectResult$ = this.select(
     this.selectLoading$.pipe(filter((loading) => loading === false)),
     this.selectSettings$,
     this.selectData$,
     (loading, settings, data) => {
        console.log('emit new data');
        return { loading, data, settings };
     }
);       

So, I want the selector to only emit values when the selectLoading$ is in state loading = false.

But I don't get selectResult$ to work properly. The flow is like this:

  1. Initially, loading = false
  2. I set loading = true
  3. I set data to selectData$
  4. The selector selectResult$ immediately emits a value. But I would not expect this as there is this filter(loading => loading === false) operator

Solution

  • The filter operator (loading === false) only works for selectloading$.
    The selectSettings$ and selectData$ can still emit, even if loading === true.

    So, you have to filter the combined result.

    this.selectResult$ = this.select({
      loading: this.selectLoading$,
      settings: this.selectSettings$,
      data: this.selectData$
    }).pipe(
      filter( ({loading}) => loading === false ), 
      map( ({loading, settings, data}) => ({ loading, data, settings }) ),
      tap( () => console.log('emit new data') ) 
    );
    

    BTW: use tap for side-effects like logging.