'*ngIf is too fast to detect subscription results

I m trying to set an countdown in my page. i give a random future date make the calculations and than show remaining time on the page. So far *ngIf is being to fast to check the value which is coming from observable. Tried also with async pipe but no luck. any ideas would be appreciated !

<div *ngIf="remainingTime" class="promo-progress-bar__info__countdown">
  <div *ngIf="remainingTime.minutes">{{remainingTime.minutes + ' / '}} {{'days' | translate}}</div>
  <div *ngIf="remainingTime.hours">{{remainingTime.hours + ' / '}}{{'hours' | translate}}</div>
  <div *ngIf="remainingTime.minutes">{{remainingTime.minutes + ' / '}}{{'minutes' | translate}}</div>
  <div *ngIf="remainingTime.seconds">{{remainingTime.seconds}}{{'seconds' | translate}}</div>
</div>


 ngOnInit(): void {
     this.getRemainingTime().subscribe(item => {
       this.remainingTime = item;
       this.cdRef.detectChanges();
    });
  }
 getRemainingTime(): Observable<DaysHours> {
 let stopDate = dayjs('Mon May 16 2022 21:09:14 GMT+0200 (Central European Summer Time)');
    this.$counter = interval(1000).pipe(
      map((x) => {
        const now = dayjs();

        let seconds = stopDate.diff(now, 'seconds')
        let days = Math.floor(seconds / (3600*24));
        let hours = Math.floor(seconds % (3600*24) / 3600);
        let minutes = Math.floor(seconds % 3600 / 60);
        seconds = Math.floor(seconds % 60);
        console.log({days, hours, minutes, seconds})
        this.cdRef.detectChanges();
        return {days, hours, minutes, seconds}
      }));
    return this.$counter;
  }


Solution 1:[1]

Use async pipe will fix your issue :

<div  *ngIf="$remainingTime | async as remainingTimeData" class="promo-progress-bar__info__countdown">
  <div *ngIf="remainingTimeData.minutes">{{remainingTimeData.minutes + ' / '}} {{'days' | translate}}</div>
  <div *ngIf="remainingTimeData.hours">{{remainingTimeData.hours + ' / '}}{{'hours' | translate}}</div>
  <div *ngIf="remainingTimeData.minutes">{{remainingTimeData.minutes + ' / '}}{{'minutes' | translate}}</div>
  <div *ngIf="remainingTimeData.seconds">{{remainingTimeData.seconds}}{{'seconds' | translate}}</div>
</div>

Inside your typescript class add a behavior subject :

  remainingTimeSubject = new BehaviorSubject<{ days: any; hours: any; minutes: any; seconds: any }>
    ({ days: null, hours: null, minutes: null, seconds: null });
    $remainingTime = this.remainingTimeSubject.asObservable();

    newRemainingTimeSubject(val:any){
      this.remainingTimeSubject.next(val);
    }

constructor() { }

your function will trigger each data changes using behavior subject method:

  newRemainingTimeSubject(val: any) {
    this.remainingTimeSubject.next(val);
  }

your function will be like this:

 getRemainingTime(): Observable<DaysHours> {
 let stopDate = dayjs('Mon May 16 2022 21:09:14 GMT+0200 (Central European Summer Time)');
    this.$counter = interval(1000).pipe(
      map((x) => {
        const now = dayjs();

        let seconds = stopDate.diff(now, 'seconds')
        let days = Math.floor(seconds / (3600*24));
        let hours = Math.floor(seconds % (3600*24) / 3600);
        let minutes = Math.floor(seconds % 3600 / 60);
        seconds = Math.floor(seconds % 60);
        console.log({days, hours, minutes, seconds})
        this.cdRef.detectChanges();
//new 
let newData = {days, hours, minutes, seconds}
this.newRemainingTimeSubject(newData)
//new 
        return {days, hours, minutes, seconds}
      }));
    return this.$counter;
  }
//dont forget to change  remainingTime to $remainingTime on your template

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1