🅰️
ngAfterViewInitでプロパティを変更したときに怒られたら
概要
AngularでコンポーネントのプロパティをngAfterViewInit
内で変更した場合、NG0100: Expression has changed after it was checked
というエラーが出て怒られる場合があります。
このままでも特に問題なく動きはしますが、気持ち悪いので消そうと思ったら調べるのに割と手間取ったので備忘録です。
結論
[Angular] (EXCEPTION: Expression has changed after it was checked) の対応方法 (Angular2, 4, 5) #Angular - Qiitaに記載の通りですが、少し古い記事なのでinject()等を使って現代的に書き直してみます。
ChangeDetectorRef
を使って明示的に変更検知を呼び出してあげればいいだけです。
export class TestComponent implements AfterViewInit {
testProperty = 0;
private cd = inject(ChangeDetectorRef);
ngAfterViewInit(): void {
testProperty = 1;
this.cd.detectChanges();
}
}
追記(2024/01/10)
以下のような状況下でも同じエラーが発生してしまいました。
- コンポーネントのプロパティにMapオブジェクトを保持
- Mapの採用理由としては反復時の順序を固定する必要があったため
- APIからのレスポンスを非同期に受け取り、
ngOnInit
などで非同期にMapオブジェクトを更新 - Mapオブジェクトの要素を
entries()
メソッドを使い、NgFor
で回す
以下、エラーが発生する場合の簡単なサンプルです。
test.component.ts
export class TestComponent implements onInit {
testProperty:Map<string,string> = new Map(); // テンプレートで使いたいMap
private exampleService = inject(ExampleService); // 何かAPIと通信するサービス
ngOnInit(): void {
// 非同期でレスポンスを受け取る
this.exampleService.getApiResponse().then((res)=>{
const body = res.body;
// 受け取ったレスポンスを元に、色々な処理をしてMapオブジェクトを生成
this.testProperty = resultMapObject; // 生成したMapオブジェクトを代入
})
}
}
test.compoent.html
<div *ngFor="let data of testProperty.entries()">
<div>{{ data[0] }}</div>
<div>{{ data[1] }}</div>
</div>
どうもコンポーネント生成時のtestProperty
とレスポンスを受け取ったあとのtestProperty
から作られるイテレータが異なるので怒られているようです。これは単純に上記の手動変更検知を行ってもダメでした。
解決策としては、Mapオブジェクトを生成してからコンポーネントのプロパティに代入する際にタプル型の配列[1]にしてしまえばOKでした。
test.component.ts
export class TestComponent implements onInit {
testProperty:[string,string][] = []; // タプル型の配列にする
private exampleService = inject(ExampleService);
ngOnInit(): void {
this.exampleService.getApiResponse().then((res)=>{
const body = res.body
// 受け取ったレスポンスを元に、色々な処理をしてMapオブジェクトを生成
this.testProperty = [...resultMapObject]; // Mapを配列にする
})
}
}
test.compoent.html
<div *ngFor="let data of testProperty">
<div>{{ data[0] }}</div>
<div>{{ data[1] }}</div>
</div>
Discussion