Angular2でコンポーネントプロパティが現在の時刻に依存する場合に発生する「expression has changed after it was checked」エラーを処理する方法
Angular2でコンポーネントプロパティが現在の時刻に依存する場合、「expression has changed after it was checked」例外を処理する方法
問題
解決策
この問題を解決するには、以下の方法があります。
ChangeDetectorRef.detectChanges() を使用する
ChangeDetectorRef
を使用して、コンポーネントツリー内の変更を明示的に検出できます。
import { Component, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
private _currentTime: Date;
constructor(private _changeDetectorRef: ChangeDetectorRef) {
this._currentTime = new Date();
}
ngOnInit() {
setInterval(() => {
this._currentTime = new Date();
this._changeDetectorRef.detectChanges();
}, 1000);
}
get currentTime(): Date {
return this._currentTime;
}
}
async
パイプを使用して、非同期的に更新されるプロパティをバインドできます。
<p>{{ currentTime | async }}</p>
Observable
を使用して、現在の時刻の変化を監視できます。
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent implements OnInit {
private _currentTime$: Observable<Date>;
constructor() {
this._currentTime$ = Observable.interval(1000)
.map(() => new Date());
}
ngOnInit() {
this._currentTime$.subscribe(currentTime => {
// コンポーネントプロパティを更新
});
}
}
これらの方法のいずれかを使用して、Angular2でコンポーネントプロパティが現在の時刻に依存する場合に発生する expression has changed after it was checked
エラーを処理できます。
import { Component, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
private _currentTime: Date;
constructor(private _changeDetectorRef: ChangeDetectorRef) {
this._currentTime = new Date();
}
ngOnInit() {
setInterval(() => {
this._currentTime = new Date();
this._changeDetectorRef.detectChanges();
}, 1000);
}
get currentTime(): Date {
return this._currentTime;
}
}
このコードは、ChangeDetectorRef.detectChanges()
を使用して、currentTime
プロパティが変更されたことを Angular に明示的に通知します。
上記以外にも、async
パイプや Observable
を使用して、現在の時刻を表示することができます。
async パイプを使用する場合
<p>{{ currentTime | async }}</p>
Observable を使用する場合
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent implements OnInit {
private _currentTime$: Observable<Date>;
constructor() {
this._currentTime$ = Observable.interval(1000)
.map(() => new Date());
}
ngOnInit() {
this._currentTime$.subscribe(currentTime => {
// コンポーネントプロパティを更新
});
}
}
これらの方法は、それぞれ異なる利点と欠点があります。使用する方法は、具体的な要件によって異なります。
ngDoCheck ライフサイクルフックを使用する
import { Component, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
private _currentTime: Date;
constructor(private _changeDetectorRef: ChangeDetectorRef) {
this._currentTime = new Date();
}
ngDoCheck() {
if (this._currentTime !== new Date()) {
this._currentTime = new Date();
this._changeDetectorRef.detectChanges();
}
}
get currentTime(): Date {
return this._currentTime;
}
}
この方法は、ChangeDetectorRef.detectChanges()
を使用するよりも効率的ですが、すべての変更を検出できるとは限りません。
@ViewChild
デコレータを使用して、子コンポーネントへの参照を取得できます。
import { Component, ViewChild } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
@ViewChild(MyChildComponent) private _childComponent: MyChildComponent;
constructor() {}
ngOnInit() {
setInterval(() => {
this._childComponent.currentTime = new Date();
}, 1000);
}
}
@Component({
selector: 'my-child-component',
templateUrl: './my-child-component.component.html',
})
export class MyChildComponent {
currentTime: Date;
constructor() {}
}
この方法は、子コンポーネントのプロパティを直接変更できるため、最も効率的な方法です。
@Input
デコレータを使用して、親コンポーネントから子コンポーネントにプロパティを渡すことができます。
import { Component, Input } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
currentTime: Date;
constructor() {}
ngOnInit() {
setInterval(() => {
this.currentTime = new Date();
}, 1000);
}
}
@Component({
selector: 'my-child-component',
templateUrl: './my-child-component.component.html',
})
export class MyChildComponent {
@Input() currentTime: Date;
constructor() {}
}
この方法は、@ViewChild
デコレータを使用するよりもシンプルですが、子コンポーネントのプロパティを変更する必要がある場合は、親コンポーネントから変更する必要があります。
ngModel
ディレクティブを使用して、テンプレート内のフォームコントロールとコンポーネントプロパティをバインドできます。
<input type="text" [(ngModel)]="currentTime" />
import { Component } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
currentTime: Date;
constructor() {}
ngOnInit() {
setInterval(() => {
this.currentTime = new Date();
}, 1000);
}
}
この方法は、フォームコントロールとコンポーネントプロパティを同期させるための最も簡単な方法です。
angular typescript time