Angular 4+:同步Observable调用前绑定值未更新问题咨询
解决Angular 4+中同步Observable场景下绑定值不更新的问题
我太懂这个坑了!之前在项目里也踩过类似的,本质是Angular变更检测的时机在搞鬼。
问题原因
Angular的变更检测默认是在异步事件队列(比如Promise回调、setTimeout、网络请求响应)完成后触发的。但如果你的Observable是同步立即解析的(比如用Observable.of()直接返回值),整个订阅回调会在当前同步执行栈里直接完成:
- 你先给绑定变量赋值(比如
this.message = '初始值') - 紧接着订阅同步Observable,回调里立刻把变量改成了Observable返回的值
- 当Angular终于轮到执行变更检测时,它看到的变量已经是最终的Observable值了,所以初始值根本没机会被渲染出来
而像网络请求这种延迟的Observable,因为回调是在异步队列里执行的,Angular会先在当前同步栈结束后触发一次变更检测(渲染初始值),等异步回调执行时再触发第二次变更检测(更新为Observable的值),所以看起来一切正常。
几种靠谱的解决方案
1. 优先用async管道(Angular推荐)
这是最简洁也最符合Angular设计理念的方式,把Observable直接绑定到模板,Angular会自动帮你处理订阅、取消订阅和变更检测,不管同步还是异步都能完美适配。
如果需要显示初始值,可以用startWith操作符:
// 组件类里 import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/startWith'; message$: Observable<string> = Observable.of('来自Observable的值').startWith('初始值');
<!-- 模板里 --> <p>{{ message$ | async }}</p>
2. 手动触发变更检测
如果必须要手动管理订阅,可以注入ChangeDetectorRef,在初始赋值后手动触发一次变更检测,让Angular先渲染初始值:
import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; @Component({ template: '<p>{{ message }}</p>' }) export class MyComponent implements OnInit { message: string; constructor(private cdr: ChangeDetectorRef) {} ngOnInit() { this.message = '初始值'; this.cdr.detectChanges(); // 手动触发变更检测,渲染初始值 Observable.of('来自Observable的值').subscribe(val => { this.message = val; }); } }
3. 把同步Observable转为异步执行
用RxJS的调度器或者delay操作符,让订阅回调进入异步队列,这样Angular会先处理初始值的变更检测:
// 方法一:用async调度器 import { Observable } from 'rxjs/Observable'; import { async } from 'rxjs/scheduler/async'; import 'rxjs/add/observable/of'; ngOnInit() { this.message = '初始值'; Observable.of('来自Observable的值', async).subscribe(val => { this.message = val; }); } // 方法二:用delay(0) import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/delay'; ngOnInit() { this.message = '初始值'; Observable.of('来自Observable的值').delay(0).subscribe(val => { this.message = val; }); }
内容的提问来源于stack exchange,提问作者Chris Moore
相关产品推荐
相关产品推荐

