Search code examples
angulartypescriptrxjsobservable

Changing a reference to an Observable used in an Angular Template?


Is there a way to change the reference of an Observable used in an Angular template?

For example suppose we have this in the template.

  {{ ( progress$ | async ) | date:'mm:ss'}}

And we want to change the the Observable that $progress points to?

Is there a way to do that?

I tried a simple experiment in this Stackblitz using setTimeout and it just demonstrates what I'm trying to do.

https://stackblitz.com/edit/stackblitz-starters-qxxkg9?file=src%2Fmain.ts

The code that changes the reference is in ngOnInit:

    setTimeout(() => {
      this.progress$ = of(0);
    }, 5000);

    setTimeout(() => {
      this.progress$ = interval(1000).pipe(map((c) => c * 1000));
    }, 8000);

The purpose of progress$ is to report how long a song has been playing. And if stop is played then the progress$ should be 0 ... and if play is pressed again, then it should restart the timer interval ...

Perhaps the right way to do this is to this is to just keep piping the Observable through function streams that change the state to what it should be?

I tried changing the pipe stream in this demo after 5 seconds but Angular continuous to render the original stream ...

  ngOnInit() {
    setTimeout(() => {
      this.progress$.pipe(map((c) => 0));
    }, 5000);


Solution

  • I updated your stackBlitz.

    https://stackblitz.com/edit/stackblitz-starters-qxxkg9?file=src%2Fmain.ts

    To controll observable, you need to use BehaviorSubject or Subject. This is my code using BehaviorSubject. This way, you can maintain a single subscription in your template and push changes through the subject.

    export class App implements OnInit {
      private progressControl$ = new BehaviorSubject<'play' | 'stop'>('play');
    
      progress$!: Observable<number>;
    
      ngOnInit() {
        this.progress$ = this.progressControl$.pipe(
          switchMap((status) => {
            if (status === 'play') {
              return interval(1000).pipe(map((c) => c * 1000));
            } else {
              return of(0);
            }
          }),
          startWith(0)
        );
    
        setTimeout(() => {
          this.progressControl$.next('stop');
        }, 5000);
    
        setTimeout(() => {
          this.progressControl$.next('play');
        }, 8000);
      }
    
      name = 'Angular';
    }