【发布时间】:2020-07-27 22:49:39
【问题描述】:
async-pipe 与 Observable 的组合存在问题,它在 OnInit 中获得了第一个值。必须是关于OnInit 发生的时间点以及模板被渲染并因此Observable 被订阅的时间点的时间问题。
考虑这个组件:
export class AppComponent implements OnInit {
subjectA$: Subject<{name:string}>;
subjectB$: Subject<{name:string}>;
constructor(
protected http: HttpClient
) {
}
ngOnInit() {
this.subjectA$ = new Subject<{name: string}>();
this.subjectA$.next({name: "A"});
this.subjectB$ = new Subject<{name: string}>();
setTimeout(() => {
this.subjectB$.next({name: "B"});
}, 0);
}
}
和模板:
<p *ngIf='subjectA$ | async as subjectA; else: nosubjectA'>
subjectA: {{subjectA.name}}
</p>
<ng-template #nosubjectA>
<p>no subjectA</p>
</ng-template>
<p *ngIf='subjectB$ | async as subjectB; else: nosubjectB'>
subjectB: {{subjectB.name}}
</p>
<ng-template #nosubjectB>
<p>no subjectB</p>
</ng-template>
这会导致
no subjectA
subjectB: B
这意味着:即使subjectA$ 在onInit 中获得了值,视图也不会更新。如果我在setTimeout 中创建第一个值,正如您在subjectB$ 中看到的那样,它可以工作并且我看到了值。虽然这是一个解决方案,但我想知道为什么会发生这种情况,是否有更好的解决方案?
我已经找到的一个解决方案是使用 BehaviorSubject 代替提供第一个值作为初始值:
this.subjectC$ = new BehaviorSubject<{name: string}>({name: "C"});
使用类似的主题C模板导致subjectC: C。
我真正可观察到的根本不是Subject,而是combineLatest-调用不同东西的结果,其中只有一个是(不幸的是,因为它使用来自@Input()-annotation 的值) 一个Subject,并在OnInit 中使用next 手动推送,如示例中所示。其余的来自http 等人。很可能我可以将组合结果包装在 BehaviourSubject 中,但它对我来说看起来既丑陋又危险,所以它比 setTimeout 方法更糟糕。但我敢打赌,有人可以帮助我并找到一个真正有用的解决方案。
此外,我更愿意避免使用BehaviorSubject,以防止开发人员被诱惑使用getValue。
【问题讨论】:
-
最近遇到了类似的问题(特别是将@Input 转换为可观察对象),我能够通过使用
shareReplay(1)(或使用缓冲区为1 的ReplaySubject)来解决可观察对象- 在ngOnInit中执行 .next 的问题似乎是它在模板实际订阅 observables 之前首先运行。将事物移动到 ngAfterViewInit(或 ngAfterContentInit)并不能真正解决问题,因为更改检测会抱怨。所以剩下的就是重放最后一个值,将其存储在BehaviorSubject中,或者也使用setTimeout。 -
好吧,这意味着我不应该太乐观地尝试除
onInit之外的其他钩子......互联网上怎么会有使用async而不是手动订阅的建议,但不要没有合适的解决方案?
标签: angular rxjs async-pipe