2017-10-04 3 views
3

Was ist die korrekte (kanonische) Möglichkeit zur Anzeige der aktuellen Zeit in Winkel 4 Change Detection System?

Das Problem ist wie folgt: aktuelle Zeit ändert sich ständig, jeden Moment, per Definition. Aber es ist nicht durch Angular 4 Change Detection System nachweisbar. Aus diesem Grund ist es meiner Meinung nach notwendig, explizit ChangeDetectorRef::detectChanges zu nennen. Beim Aufruf dieser Methode ändert die aktuelle Zeit jedoch ihren Wert. Dies führt zu ExpressionChangedAfterItHasBeenCheckedError. Im folgende Beispiel (see live) Dieser Fehler tritt withing ein paar Sekunden nach dem Laden der Seite:Angular 4 Display aktuelle Zeit

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); 
    } 
} 

Antwort

4

Zunächst einmal brauchen Sie nicht ChangeDetectorRef.detectChanges() in Ihrem Intervall zu nennen, weil Winkel ist Zone.js welche Affen Patches der Browser setInteral Methode mit einer eigenen Verwendung. Daher weiß angular sehr gut, welche Änderungen während eines Intervalls auftreten.

Sie sollten die Zeit in Ihrem Intervall wie folgt festgelegt:

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

    constructor() { 
     setInterval(() => { 
      this.date = new Date(); 
     }, 1); 
    } 
} 

Aber Sie sollten sich die Zeit auf einer so hohen Rate nicht aktualisieren, weil es zu einer schlechten Performance führen würde, weil jedes Mal das Datum updates angular führt eine Änderungserkennung am Komponentenbaum durch.

Wenn Sie das DOM mit einer sehr hohen Rate aktualisieren mögen, sollten Sie runOutsideAngular von NgZone verwenden und den DOM manuell die Renderer2 Verwendung aktualisieren.

Zum Beispiel:

@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); 
    }); 
    } 
} 
+0

Vielen Dank für diese ausführliche Erklärung, verdeutlicht es sehr. Ich wollte sicherstellen, dass dieses Beispiel ein fundamentaler Winkelfehler ist: Warum müssen wir explizit 'runOutsideAngular' aufrufen und die Intervalle außerhalb des Systems zur Erkennung von Winkeländerungen manuell manuell setzen? Widerspricht es nicht dem Rahmenparadigma? – BOPOHOB

+0

Das ist kein Fehler. Die Änderungserkennung ist nicht für Aktualisierungen mit einer so hohen Rate vorgesehen, deshalb hat angular den Dienst "NgZone", der eine Möglichkeit bietet, Berechnungen manuell durchzuführen oder die Ansicht zu aktualisieren, ohne den Änderungsdetektionsalgorithmus aufzurufen. – cyrix

+0

Lösung vom ersten Beispiel kann ExpressionChangedAfterItHasBeenCheckedError verursachen, d. H. Es gibt keine Möglichkeit, Zeit ohne direkten Elementzugriff anzuzeigen. Es gibt einen Fehler, den ich meine ... Korrekte Lösung gehen äußere Winkelschablone. In Google-Lösung von hier https://github.com/urish/angular2-moment ist es von Rohr eingewickelt, die viel besser aussieht – BOPOHOB

0

Pfeilfunktion.

constructor(cd: ChangeDetectorRef){ 
    setInterval(() => { cd.detectChanges(); }, 1); 
    }