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);
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';
}