Angular 8组件动态数据绑定报错:ExpressionChangedAfterItHasBeenCheckedError
解决Angular 8中
ExpressionChangedAfterItHasBeenCheckedError问题 这个错误在Angular开发模式下很常见,本质是Angular在一次变更检测周期完成后,发现视图绑定的属性值发生了变化,打破了数据一致性的检查规则。结合你的OnPush变更检测策略和代码逻辑,我给你几个可行的解决方案:
1. 用NgZone包裹异步更新逻辑
异步请求的回调有时候会跑出Angular的变更检测上下文,导致数据更新时机和检测周期不匹配。你可以注入NgZone,把数据更新操作包裹在Angular的上下文内执行:
首先在构造函数中注入NgZone:
constructor( public cl: Services, private cd: ChangeDetectorRef, private zone: NgZone // 新增注入 ) { this.options = { width: '258', multiple: true, tags: false }; }
然后修改changed方法的订阅回调:
changed(search: any) { return this.cl.searchFunc(search).subscribe( res1 => { this.zone.run(() => { // 把更新逻辑包裹在zone.run里 this.select2data = res1.data; this.cd.markForCheck(); }); }, error => { console.log('error = ', error); }); }
2. 改用BehaviorSubject + async管道(推荐)
这种方式更贴合Angular的响应式编程理念,能自动处理变更检测,不需要手动调用markForCheck:
首先在组件内定义一个BehaviorSubject来管理数据:
import { BehaviorSubject } from 'rxjs'; // ... export class Select2Component implements OnInit { @Input() field: any = {}; @Input() form: FormGroup; public exampleData: Array<Select2OptionData>; public options: Options; public value: string[]; private select2dataSubject = new BehaviorSubject<any[]>([]); // 定义Subject存储数据 select2data$ = this.select2dataSubject.asObservable(); // 暴露可订阅的Observable public selected: string; constructor(public cl: Services) { // 可以移除ChangeDetectorRef依赖 this.options = { width: '258', multiple: true, tags: false }; } changed(search: any) { return this.cl.searchFunc(search).subscribe( res1 => { this.select2dataSubject.next(res1.data); // 发送新数据 }, error => { console.log('error = ', error); }); } }
然后修改模板,用async管道绑定数据:
<ng-select2 [data]="select2data$ | async" [options]="options" [width]="500" [formControlName]="field.code" (keyup)="changed($event.target.value)"> </ng-select2>
async管道会自动订阅Observable,当新数据到来时,Angular会自动触发变更检测(即使是OnPush模式),从根源上避免了检测周期后数据变化的问题。
3. 临时禁用开发模式检查(不推荐)
如果你只是想在开发阶段暂时屏蔽警告,可以在main.ts中关闭Angular的开发模式检查,但这只是治标不治本,生产模式下该检查本来就会关闭:
import { enableProdMode } from '@angular/core'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } else { // 关闭开发模式的变更检测一致性检查 import { ɵdisableDebugChecks } from '@angular/core'; ɵdisableDebugChecks(); }
为什么原来的markForCheck没起作用?
你的异步回调是在当前变更检测周期结束后执行的,调用markForCheck只是标记组件需要在下一次检测时更新,但开发模式下Angular会额外对比上一次周期结束时的值和当前值,发现不一致就抛出错误。上面的两种方案要么把更新拉回当前检测上下文,要么用响应式方式让变更检测更可控。
内容的提问来源于stack exchange,提问作者afeef
相关产品推荐
相关产品推荐

