Search code examples
angularrxjscountdowncountdowntimer

Angular 2 - Countdown timer


I am willing to do a countdown timer in Angular 2 that start from 60 (i.e 59, 58,57, etc...)

For that I have the following:

constructor(){
  Observable.timer(0,1000).subscribe(timer=>{
  this.counter = timer;
});
}

The above, ticks every second, which is fine; however, it goes in an ascending order to an unlimited number. I am not sure if there is a way to tweak it so I can have a countdown timer.


Solution

  • There are many ways to achieve this, a basic example is to use the take operator

    import { Observable, timer } from 'rxjs';
    import { take, map } from 'rxjs/operators';
    
    @Component({
       selector: 'my-app',
       template: `<h2>{{counter$ | async}}</h2>`
    })
    export class App {
       counter$: Observable<number>;
       count = 60;
    
       constructor() {
         this.counter$ = timer(0,1000).pipe(
           take(this.count),
           map(() => --this.count)
         );
       }
    }
    

    A better way is to create a counter directive!

    import { Directive, Input, Output, EventEmitter, OnChanges, OnDestroy } from '@angular/core';
    
    import { Subject, Observable, Subscription, timer } from 'rxjs';
    import { switchMap, take, tap } from 'rxjs/operators';
    
    @Directive({
      selector: '[counter]'
    })
    export class CounterDirective implements OnChanges, OnDestroy {
    
      private _counterSource$ = new Subject<any>();
      private _subscription = Subscription.EMPTY;
    
      @Input() counter: number;
      @Input() interval: number;
      @Output() value = new EventEmitter<number>();
    
      constructor() {
    
        this._subscription = this._counterSource$.pipe(
          switchMap(({ interval, count }) =>
            timer(0, interval).pipe(
              take(count),
              tap(() => this.value.emit(--count))
            )
          )
        ).subscribe();
      }
    
      ngOnChanges() {
        this._counterSource$.next({ count: this.counter, interval: this.interval });
      }
    
      ngOnDestroy() {
        this._subscription.unsubscribe();
      }
    
    }
    

    Usage:

    <ng-container [counter]="60" [interval]="1000" (value)="count = $event">
      <span> {{ count }} </span>
    </ng-container>
    

    Here is a live stackblitz