Angular: *NGIF使用与异步管的con缩示例失败
如果我有一个可观察的随着时间的推移释放值:
values$ = from([1, 2, 3, 'done'])
.pipe(
concatMap((x) => of(x).pipe(delay(1000)))
);
并且我有一个函数返回对该可观察的
:
getOutputs(): Observable<'done' | number> {
return this.values$;
}
并通过函数订阅observable
在模板中使用*NGIF
和async
:
<div *ngIf="getOutputs() | async as val">
<hello name="{{ val }}"></hello>
</div>
预期行为:浏览器显示'Hello 1!','Hello 2!','Hello 2!','Hello 3!','您好!',大约一秒钟的间隔。
相反,如果我将最新值存储在caping ubyubject
中,并通过该cravive umagiSubject
在ngoninit
中循环所有值:
outputs$ = new BehaviorSubject<number | 'done' | null>(null);
ngOnInit(): void {
this.subscriptions.add(
from<[number, number, number, 'done']>([
1,
2,
3,
'done',
]).subscribe((val) => this.outputs$.next(val))
);
}
当然,该行为是不同:这些值全部都发送到cistionUbject
,输出$ .VALUE
非常快地变为“完成”。因此,以后出现的任何事情都只会“完成”。也期望。
'Hello hello!
getOutputs(): Observable<null | 'done' | number> {
return this.outputs$;
}
如果我更改getOutputs()
以使用this.outputs $
而不是 >较早使用的是:
getOutputs(): Observable<null | 'done' | number> {
return this.outputs$
.pipe(
concatMap((x) => of(x).pipe(delay(1000)))
);
}
“完成”一遍又一遍地发送,一秒钟(可以通过tap(Console.log)
)看到,但是模板什么也没显示。这是出乎意料的:我认为HTML会显示“你好!”。
为什么会发生这种情况?
参见这个stackblitz 。
If I have an Observable
releasing values over time:
values$ = from([1, 2, 3, 'done'])
.pipe(
concatMap((x) => of(x).pipe(delay(1000)))
);
And I have a function returning access to that Observable
:
getOutputs(): Observable<'done' | number> {
return this.values$;
}
And subscribe to the Observable
through a function in the template using *ngIf
and async
:
<div *ngIf="getOutputs() | async as val">
<hello name="{{ val }}"></hello>
</div>
The behavior is expected: the browser shows 'Hello 1!', 'Hello 2!', 'Hello 3!', 'Hello done!', with an interval for each of about a second.
If, instead, I store the latest value in a BehaviorSubject
and cycle all of the values through that BehaviorSubject
in ngOnInit
:
outputs$ = new BehaviorSubject<number | 'done' | null>(null);
ngOnInit(): void {
this.subscriptions.add(
from<[number, number, number, 'done']>([
1,
2,
3,
'done',
]).subscribe((val) => this.outputs$.next(val))
);
}
The behavior is of course different: the values are all sent to the BehaviorSubject
, and outputs$.value
becomes 'done' very quickly. So anything coming along later and subscribing would only get 'done'. Also expected.
If I change getOutputs()
to use this.outputs$
instead, I just get 'Hello done!':
getOutputs(): Observable<null | 'done' | number> {
return this.outputs$;
}
But if I add the same concatMap
used earlier, like this:
getOutputs(): Observable<null | 'done' | number> {
return this.outputs$
.pipe(
concatMap((x) => of(x).pipe(delay(1000)))
);
}
'done' gets sent over and over, once a second (which can be seen through tap(console.log)
), but the template shows nothing. This is unexpected: I would think that the HTML would show 'Hello done!'.
Why is this happening?
See this Stackblitz.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Tl; dr
这是由角度处理变化检测方式引起的。
Angular将定期检查您的视图是否与模型中的数据有关,实际上每秒都连续调用您的
getOuputs()
方法!简而言之,Angular的更改检测
考虑您的
app.component.html
模板:在这里,Angular将定期重新评估
getOutputs()|异步
,“几次”,直到您的应用程序处于稳定状态。但是,对于每次评估,您都将返回新的,唯一的
可观察到的
,因为您 create 的getOutputs
方法中:的在您
可观察 ,对此感到抱歉...)
...那么您的申请将完全按照您的期望!
还有更多的探索
,但是为什么要删除延迟,但每次都会产生不同的
可观察到的
呢?让我们考虑
getOutputs
下面的替代实现:这是因为我(故意
TL;DR
This is caused by how Angular handles change detection.
Angular will regularly check that your view is up-to-date with the data in your model, and is in fact continuously calling your
getOuputs()
method, once every second!Angular's Change Detection in a nutshell
Consider your
app.component.html
template:Here, Angular will regularly re-evaluate
getOutputs() | async
, a "couple of times", until your application is in a stable state.However, for each evaluation, you are returning a new, unique
Observable
, because you create that new, uniqueObservable
in yourgetOutputs
method:Therefore, if you where to create another member like that:
(welp, StackOverflow doesn't support
diff
syntax highlighting, sorry about that...)... then your application would behave exactly as you expect!
Some more exploration
But then why does removing the delay, yet yielding a different
Observable
every time also works?Let's consider the alternative implementation for
getOutputs
below:It's because I (purposely ????) overlooked something earlier:
Angular isn't simply re-evaluating
getOutputs()
until your component is stable, but actuallygetOutputs() | async
, which translates roughly asasync(getOutput())
in a pure TypeScript world.What is happening here is also somewhat simple:
Contrary to the previous case, where the
AsyncPipe
, when having to wait for adelay
edObservble
, won't be definitely stable; thatAsyncPipe
yields a value immediately and yields the same ('done'
) every time; Angular considers your component "stable" and proceeds with updating the view.If you debug the code attentively (or drop a
console.log
at the top ofgetOutputs()
's body), you can notice thatgetOutputs()
is then still called 4 times in rapid succession, one for each evaluation ofasync(getOutputs())
.Switching to a more conservative
changeDetection
strategy for your component as follows:... would allow
getOutputs()
to only be executed once in that case!