我有一个简单的组件模板:
<button (click)="emitNextValue()">Next Value</button>
<p *ngIf="text$ | async">
{{ text$ | async }}
</p>
和组件代码:
export class AppComponent {
count = 1;
text$ = new Subject<string>();
emitNextValue(): void {
this.text$.next(`Value ${this.count}`);
this.count += 1;
}
}
可以看到,只有当text$
Subjects发出值时,模板中的<p>
才会显示。点击"下一个值"按钮将调用主题上的next
,因此应该显示段落。
问题是<p>
只显示在第二个单击,我认为这是不正确的。
知道我错过了什么,还是这是真正的bug?
Demo在stackblitz上可用。
这就是所谓的"晚订阅者"问题。当传入的Rx值在订阅发生之前到达时,会发生此问题。
如果你从html中删除*ngIf指令,它会正常工作。
<p>
{{ text$ | async }}
</p>
查看更多信息
订阅晚了。通常情况下,您可以通过shareReplay(1)
操作符或使用ReplaySubject(1)
主题使订阅重播上次事件。但在你的特殊情况下,有一个更好的解决方案
<p *ngIf="text$ | async as text">
{{ text }}
</p>
ngIf允许"store"表达式的结果对于这种情况非常有用
事实上,<p>
元素是在第一个事件被通知后才创建的。
因此,在通知第一个事件之后,Dom中就有了元素,因此也有了{{ text$ | async }}
。然后,当发生第二次单击时,将通知第二个事件并触发绑定。
如果您将Subject
替换为new ReplaySubject<any>(1)
,您将在第一次单击后显示文本。
原因是ReplaySubject
一旦被订阅,就会重播它发出的最后一个值。因此,即使文本绑定发生在通知第一个事件之后,被通知的值也会被主题重放,因此会显示在<p>
元素中。