angularrxjsngxs

Using NGXS state data in a forkjoin


Within an Angular component I have to retrieve data from an API then use it with some state data from an NGXS state variable. In my component I have this:

  @Select(MyObjectState.get)
  public readonly myobjects$: Observable<MyObject[]>;

What I want to do is this:

    forkjoin([this.apiService.GetSomeData(), this.myobjects$])
    .pipe(
        map(([mydata, myobjects]) => {
            ...do some stuff
        })
    )
    .subscribe((result) => {
        ...more stuff
   })

The issue is that the 'forkjoin' never completes and calls the map operator. I'm not sure if the myobjects$ Observable just doesn't complete or what but it just hangs. I tested this by replacing myobjects$ with of([]) and then the 'forkjoin' completes.

How can I combine my state data with my api data?


Solution

  • forkJoin will only emit when all of its sources complete. Depending on when you want your subscribe logic to execute, you have a few options.

    Execute One time only

    forkjoin([
      this.apiService.GetSomeData(),
      this.myobjects$.pipe(take(1)), // <--- take(1) 
    ]).pipe(
      /* ... */
    ).subscribe(
      result => { /* ... */ }
    );
    

    You can use the take operator to cause your observable to complete after one emission, allowing forkJoin to emit the results.

    Execute on every source emission

    combineLatest([this.apiService.GetSomeData(), this.myobjects$]).pipe(
      /* ... */
    ).subscribe(
      result => { /* ... */ }
    );
    

    If you want to run your subscribe logic every time any source emits, you can use combineLatest. This doesn't require the sources to complete, it only requires that each source emit at least one value.

    Get data then execute whenever myObjects$ emits

    this.apiService.GetSomeData().pipe(
      switchMap(mydata => this.myobjects$.pipe(
        map(myobjects => /* ... */
      ))
    ).subscribe(
      result => { /* ... */ }
    );
    

    You can also use switchMap. The above code takes emissions from your api service call, then maps them to a new observable. The emissions from this "inner observable" will be emitted.

    You can use a nested .pipe() so that your inner logic has access to both the mydata emission as well as the myobjects emissions.