2017-10-04 99 views
3

在角度4变化检测系统中显示当前时间的正确方法是什么?

问题如下:根据定义,当前时间不断变化,每个时刻都在变化。但是它不能被角度4变化检测系统检测到。出于这个原因,在我看来,有必要明确呼吁ChangeDetectorRef::detectChanges。但是,在调用此方法的过程中,当前时间自然会改变其值。这导致ExpressionChangedAfterItHasBeenCheckedError。在下面的例子(see live)这个错误出现在页面加载后withing几秒钟:角度4显示当前时间

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

回答

4

所有你不需要调用ChangeDetectorRef.detectChanges()您的区间内,因为角度使用Zone.js这猴子补丁的浏览器setInteral方法与自己的第一次。因此角度很清楚在一个时间间隔内发生的变化。

您应该设置时间间隔你里面是这样的:

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

但你不应该在如此高的速度更新时间,因为它会导致性能比较差,因为每次的日期更新角度在组件树上执行更改检测。

如果要更新以非常高的速度DOM,你应该使用runOutsideAngularNgZone和使用Renderer2手动更新DOM。

例如:

@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

非常感谢您对这样的详细解释,澄清了很多。我想确保这个例子是一个基本的Angular缺陷:为什么我们必须显式调用'runOutsideAngular'并在Angular change检测系统之外手动显式设置时间间隔?这与框架范式不矛盾吗? – BOPOHOB

+0

这不是一个缺陷。更改检测并不是更新速度如此之快,这就是为什么angular有'NgZone'服务,它提供了一种手动进行计算或更新视图的方式,而无需调用更改检测算法。 – cyrix

+0

第一个示例的解决方案可能会导致ExpressionChangedAfterItHasBeenCheckedError,即无法显示没有直接元素访问权限的时间。有缺陷我的意思是...正确的解决方案走出角模板。在谷歌解决方案从这里https://github.com/urish/angular2-moment它包裹了管看起来好多了 – BOPOHOB

0

使用箭头功能。

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