Home > other >  Observable completing too soon
Observable completing too soon

Time:12-29

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, emit false

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!            
  • Related