Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 display current time

Tags:

What is the correct (canonical) way to display current time in angular 4 change detection system?

The problem is as follows: current time changes constantly, each moment, by definition. But it is not detectable by Angular 4 change detection system. For this reason, in my opinion, it's necessary to explicitly call ChangeDetectorRef::detectChanges. However, in the process of calling this method, current time naturally changes its value. This leads to ExpressionChangedAfterItHasBeenCheckedError. In the following example (see live) this error appears withing a few seconds after loading the page:

import { Component, ChangeDetectorRef } from '@angular/core'; @Component({     selector: 'my-app',     template: `{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />         {{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />         {{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />         {{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />         {{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />         {{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />         {{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />         {{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}<br />{{ now }}` }) export class AppComponent {     get now() : string { return Date(); }     constructor(cd: ChangeDetectorRef) {         setInterval(function() { cd.detectChanges(); }, 1);     } } 
like image 232
BOPOHOB Avatar asked Oct 04 '17 10:10

BOPOHOB


2 Answers

First of all you don't need to call ChangeDetectorRef.detectChanges() inside your interval, because angular is using Zone.js which monkey patches the browsers setInteral method with its own. Therefore angular is well aware of the changes happening during an interval.

You should set the time inside your interval like this:

import { Component } from '@angular/core'; @Component({     selector: 'my-app',     template: `{{ now }}` }) export class AppComponent {     public now: Date = new Date();      constructor() {         setInterval(() => {           this.now = new Date();         }, 1);     } } 

But you shouldn't update the time on such a high rate, because it would lead to poor perfomance, because everytime the date updates angular performs a changedetection on the component tree.

If you want to update the DOM at a very high rate, you should use runOutsideAngular from NgZone and update the DOM manually using the Renderer2.

For example:

@Component({   selector: 'my-counter',   template: '<span #counter></span>' }) class CounterComponent implements OnChange {   public count: number = 0;    @ViewChild('counter')   public myCounter: ElementRef;    constructor(private zone: NgZone, private renderer: Renderer2) {     this.zone.runOutsideAngular(() => {       setInterval(() => {         this.renderer.setProperty(this.myCounter.nativeElement, 'textContent', ++this.count);       }, 1);     });   } } 
like image 196
cyr_x Avatar answered Oct 20 '22 05:10

cyr_x


As per Angular documentation on the DatePipe Link, Date.now() can be used.

import { Component } from '@angular/core'; @Component({     selector: 'my-app',     template: `{{ now | date:'HH:mm:ss'}}` }) export class AppComponent {     now:number;      constructor() {         setInterval(() => {           this.now = Date.now();         }, 1);     } } 
like image 40
mrOak Avatar answered Oct 20 '22 05:10

mrOak