Angular 5 – Basic timer object not updating in the UI

Issue

I am working on an application built in Angular 5, which needs a basic mm:ss countdown timer, which will eventually be set dynamically. I’ve tried many different solutions, but all behave in the same way.

I’m starting hard coded just to get it running, and have added a basic function:

time: number = 60;
interval;

startTimer() {
    this.interval = setInterval(() => {
        if (this.time > 0) {
            this.time--;
        } else {
            this.time = 60;
        }
    }, 1000)
}

In the front end, I call the function with a button:

<button (click)='startTimer()'>Start Timer</button>
<p>{{ time }} seconds remaining....</p>

When the page loads up, the number 60 can be seen. But it doesn’t count down. When you press the button, the number changes to whichever second it is up to. So for example it will display 60, click it 2 seconds later and the number will change to 58. So the timer is running the background, but the number is not updating in the UI.

I’m new to Angular, so am probably msising something basic. I’ve seen other solutions which require creating a Pipe or a subscription, but all those I’ve tried seem to behave in the same way as this. Any pointers would be appreciated!

Solution

I’d say it’s better to use RxJS instead of setInterval() method. Try the following using RxJS timer function and take and map operators.

Controller

timer$: Observable<number>;

startTimer(counter: number) {
  this.timer$ = timer(0, 1000).pipe(
    take(counter),
    map(_ => --counter)
  );
}

Template

<input type="number" #timeInput placeholder="Enter time in seconds"/><br>
<button (click)='startTimer(timeInput.value)'>Start Timer</button>

<ng-container *ngIf="(timer$ | async) as timer">
  <p>{{ timer }} seconds remaining....</p>
</ng-container>

Working example: Stackblitz

Update: Older version of RxJS (<=v5)

There’s a stark difference in the import and usage of RxJS functions and operators b/n v5+ and v6+. The following should work for RxJS v5.

Controller

import 'rxjs/add/observable/timer';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';

timer$: any;

startTimer(counter: number) {
  this.timer$ = Observable.timer(0, 1000).take(counter).map(_ => --counter);
}

Template

<input type="number" #timeInput placeholder="Enter time in seconds"/><br>
<button (click)='startTimer(timeInput.value)'>Start Timer</button>

<ng-container *ngIf="(timer$ | async) as timer">
  <p>{{ timer }} seconds remaining....</p>
</ng-container>

Answered By – ruth

Answer Checked By – Marilyn (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.