stephan.peters
stephan.peters

Reputation: 1107

create observables from an array of data and chain all these together

I have the following requirement:

The context is a guard in Angular which uses canActivate() returning an Observable<boolean>

I want to avoid following construction in the guard (pseudo code):

// the actions to check
var actions = ['x', 'y', 'z'];

canActivate() : Observable<boolean> {
    return this.performAction(actions[0]).subscribe(result => {
      if(result){
        this.performAction(actions[1]).subscribe(result2 => {
           if(result2){
               this.performAction(actions[2]).subscribe(result3 => {
                 ...
               };
           }
       });
   }
   //result = false;
 };
}

Upvotes: 4

Views: 1399

Answers (2)

Mazrick
Mazrick

Reputation: 1159

Updated answer for rxjs 7.x (7.5.4)

This answer does also satisfy the OP's requirement of:

  • further observables should not have to proceed

To get this working I had to use a new parameter added to the takeWhile operator(rxjs 6+) which returns the value that caused the takeWhile operator to complete the observable.

Working code example: https://stackblitz.com/edit/rxjs5-85mw81?file=index.ts

  const arrayOfValues = ['x', 'y', 'z'];

  const performAction = (action): Observable<boolean> => {
    if (action === 'y') {
      return of(false);
    }
    return of(true);
  }

  const observable = of(...arrayOfValues).pipe(
    concatMap(a => performAction(a)),
    takeWhile(result => result !== false, true),
    takeLast(1));
    
  observable.subscribe(console.log);

Upvotes: 0

martin
martin

Reputation: 96891

You can do it like this:

import { Observable, Subject, ReplaySubject } from 'rxjs';

const actions = ['x', 'y', 'z'];

const performAction = (action): Observable<boolean> => {
  if (action === 'y') {
    return Observable.of(false);
  }
  return Observable.of(true);
}

const observable = Observable.from(actions)
  .concatMap(a => performAction(a))
  .multicast(new ReplaySubject(1),
    s => s.takeWhile(result => result !== false).concat(s.take(1))
  );

observable.subscribe(console.log);

See live demo (open console): https://stackblitz.com/edit/rxjs5-hnzwtt?file=index.ts

The most important part is the multicast operator that passes through everything until it receives false. Then this false value is the last one before completing the chain (thanks to concat).

The output is:

true
false

Upvotes: 1

Related Questions