Search code examples
javascriptangularuser-interfacetimerangular-directive

Timer - Count up timer for Angular 8


I am trying to display a timer as shown below in this screenshot. To count up constantly to show how long since this entry has been made.

enter image description here

I couldn't find too many options for doing it in Angular. I found this solution in Angular JS. https://siddii.github.io/angular-timer/ I need the timer exactly like this. But I was not able to port it into Angular or I couldn't. So tried the other alternatives available and found these still they are all also not working.

https://www.npmjs.com/package/ng2-simple-timer

https://www.npmjs.com/package/ngx-timer

The closest I got to was this ngx-timer's Countup-timer. https://www.npmjs.com/package/ngx-timer the timer works fine and its able to start from a given date as per my requirement. startTimer(startDate). But it has an known issue which is still unresolved that is it applies itself to the last known index of the timer element thereby only one timer running in a whole list of timers.You can notice that in the above given screenshot itself.

its a known bug. https://github.com/Y4SHVINE/ngx-timer-lib/issues

So can somebody help me with a solution or some tweaks to one of these solutions to make it work.

Thanks.


Solution

  • I would do this by writing a function to return the difference between the current time and the creation time of an object.

    My initial thought was to return the difference as a Date object and then format the result. I soon ran into problems when formatting different time spans, as a date is not the same thing as a time span.

    So instead of trying to format a Date that is pretending to be a time span, I would create my own interface.

    export interface TimeSpan {
      hours: number;
      minutes: number;
      seconds: number;
    }
    

    By doing this we retain control of how time spans >= 1 day are handled, and avoid time zone issues.

    I would use OnPush change detection to keep control over when change detection is run:

    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class AppComponent {
      constructor(private changeDetector: ChangeDetectorRef) {}
    }
    

    I would then kick off an RxJS interval in ngOnInit(), making sure to unsubscribe when we're finished:

    private destroyed$ = new Subject();
    
    ngOnInit() {
      interval(1000).subscribe(() => {
        this.changeDetector.detectChanges();
      });
    }
    
    ngOnDestroy() {
      this.destroyed$.next();
      this.destroyed$.complete();
    }
    

    The HTML would then get the elapsed time from a function on my component:

    getElapsedTime(entry: Entry): TimeSpan {        
      let totalSeconds = Math.floor((new Date().getTime() - entry.created.getTime()) / 1000);
    
      let hours = 0;
      let minutes = 0;
      let seconds = 0;
    
      if (totalSeconds >= 3600) {
        hours = Math.floor(totalSeconds / 3600);      
        totalSeconds -= 3600 * hours;      
      }
    
      if (totalSeconds >= 60) {
        minutes = Math.floor(totalSeconds / 60);
        totalSeconds -= 60 * minutes;
      }
    
      seconds = totalSeconds;
    
      return {
        hours: hours,
        minutes: minutes,
        seconds: seconds
      };
    }
    

    This function starts off by getting the total number of seconds elapsed since the creation date, and then works out the hour, minute, and second components.

    Where entry is the object that contains some Date instance indicating its creation time. For my demo, I am using this interface:

    export interface Entry {
      created: Date;
      id: string;
    }
    

    The elapsed time span can then be retrieved for each instance inside an *ngFor like this:

    <span *ngIf="getElapsedTime(entry) as elapsed">
      {{elapsed.hours}} h {{elapsed.minutes}} m {{elapsed.seconds}} s
    </span>
    

    DEMO: https://stackblitz.com/edit/angular-p1b9af