Angular 9 ngOnChanges触发后视图不更新 为何需使用ChangeDetectorRef?
问题根因
造成这个现象的常见原因有3种:
- 子组件设置了
ChangeDetectionStrategy.OnPush变更检测策略:OnPush模式下Angular仅会在@Input属性引用变化、组件内部触发DOM事件、手动调用变更检测API三种场景下触发组件的模板更新。你在ngOnChanges执行后期才修改isLoaded的值,此时输入属性触发的变更检测已经执行完毕,没有新的触发源就不会同步更新模板。 ngOnChanges内的执行时序问题:你当前代码在ngOnChanges中先将isLoaded设为false,立刻调用了detectChanges()完成本轮变更检测,之后才执行doSth把isLoaded设为true,后续的赋值没有对应的变更检测触发,自然不会同步到模板。doSth内部存在异步逻辑:如果doSth里包含定时器、未被Angular封装的异步请求、第三方库回调等逻辑,isLoaded = true的赋值会运行在Angular的变更检测区域之外,Angular感知不到这次赋值,也就不会触发模板更新。
可行解决方案
方案1:调整手动变更检测的调用时机
如果确认要保留isLoaded普通变量的写法,把手动调用变更检测的逻辑移到isLoaded赋值之后即可:
ngOnChanges(changes: SimpleChanges): void { this.isLoaded = false; this.doSth(); } private doSth() { // 原有业务逻辑 this.isLoaded = true; // 赋值完成后再标记需要更新 this.cd.markForCheck(); }
如果doSth包含异步逻辑,要把markForCheck放到异步回调的最末尾。
方案2:使用RxJs+async管道(更推荐)
你补充的方案是Angular生态下更规范的响应式写法,async管道会自动订阅数据流,在收到新值时主动触发当前组件的变更检测,不需要手动管理变更检测的调用:
// 子组件类代码 public isLoaded$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); ngOnChanges(changes: SimpleChanges): void { this.isLoaded$.next(false); this.doSth(); // 如果doSth有异步逻辑,要把下面这行移到异步回调内 this.isLoaded$.next(true); }
<!-- 子组件模板代码 --> <div *ngIf="isLoaded$ | async"> <custom-component [data]="data"></custom-component> </div>
内容的提问来源于stack exchange,提问作者dark
相关产品推荐
相关产品推荐

