I have an element using async pipe to subscribe to a Behavior Subject
showDiv$ = new BehaviorSubject<boolean>(false);
showDivObv$ = this.showDiv$.asObservable().pipe(
tap(() => console.log('here')),
filter(Boolean),
tap(() => console.log('here2')),
takeUntil(timer(1000))
);
<div *ngIf="showDivObv$ | async" >
....
</div>
the issue is that the first console log is called when the angular page first loads but later in the app when I press a want, I want to push true onto the subject showDiv$.next(true)
when I do this - the div never shows up and then second console log is never fired.
can someone explain to me why this is? The only thing I can think is that the observable is firing its complete method and async pipe is then never true
The solve I am going for is when a button is pushed - show a Div for 2 seconds and then hide it
CodePudding user response:
I think the why second tap not firing coz operator takeUntil already reach the timer 1000. If you hit the button before it reach will shown on the logs.
import { Component, OnInit } from "@angular/core";
import { BehaviorSubject, timer } from "rxjs";
import { filter, takeUntil, tap } from "rxjs/operators";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit {
showDiv$ = new BehaviorSubject<boolean>(false);
showDivObv$ = this.showDiv$.asObservable();
ngOnInit(): void {
this.showDivObv$.pipe(
tap(() => console.log('here')),
filter(Boolean),
tap(() => console.log('here2')),
takeUntil(timer(1000))
).subscribe(b => console.log({b}));
}
clickBtn() {
console.log('hellow', this.showDiv$.value)
this.showDiv$.next(!this.showDiv$.value);
}
}
// console
// here
// hellow
// false
// here
// here2
// {b: true}
// hellow
// true
// hellow
// false
CodePudding user response:
When you use takeUntil(timer(1000))
, it means your observable will not emit any more after 1000ms.
I think you want the following behavior:
after click, emit
true
, then after 2000ms, emitfalse
You can use timer()
to create an observable that emits true
immediately, then emits false
after a delay:
timer(0, 2000).pipe( // emits sequential integers after initial delay at interval
map(n => !n), // convert 0 => true, 1 => false
take(2) // we only want two emissions, 'true' then 'false'
);
To tie this in with the button click event, you can use a plain Subject
like this:
showDivFor2000ms$ = new Subject<void>();
showDiv$ = this.showDivFor2000ms$.pipe(
exhaustMap(() => timer(0, 2000).pipe(
map(val => !val),
take(2)
))
);
Here each time exhaustMap
receives an emission, it will subscribe to the timer and emit its emissions.
<button (click)="showDivFor2000ms$.next()"> Show Div </button>
<div *ngIf="showDiv$ | async">
This is a very special div!