嵌套在 ngIf 中时,异步管道订阅无法正常工作
Posted
技术标签:
【中文标题】嵌套在 ngIf 中时,异步管道订阅无法正常工作【英文标题】:Async pipe subscription not work correctly when nested inside ngIf 【发布时间】:2019-01-29 03:48:00 【问题描述】:我有一个快速搜索框,我想要一个加载动画。我使用ng-template
和ngIf
来显示/隐藏这个动画。我有一些 li 嵌套在同一个 div 中,该 div 使用异步管道订阅搜索结果 Observable 来显示结果。当父 div 上没有 *ngIf
时,此异步管道效果很好,但当我应用 ngIf
时,它似乎不再订阅。这是预期的行为吗?还是我做错了什么?
我的标记看起来像这样。
<input #searchBox type="text" (keyup)="itemLoading=true;searchTerm$.next(searchBox.value)"/>
<div *ngIf="!itemLoading else loading">
<!--Remove ngIf then items will display correctly when search-->
<!-- <div> -->
<ul>
<li *ngFor="let item of result$ | async ">item</li>
</ul>
</div>
<ng-template #loading>
<p>LOADING...</p>
</ng-template>
我正在使用 switchMap 来运行搜索:
private source = of(['apple', 'pear', 'banana']).pipe(delay(500));
searchTerm$ = new Subject<string>();
result$: Observable<string[]>;
itemLoading = false;
constructor()
this.result$ = this.searchTerm$.pipe(
tap(term => console.log('search term captured: ' + term)),
debounceTime(300),
distinctUntilChanged(),
switchMap(() => this.source.pipe(
tap(_ =>
console.log('source reached');
this.itemLoading = false;
)
))
);
当 ngIf
出现在父 div 中时,“到达源”消息永远不会登录到控制台,并且加载模板一直挂在那里。
这是我正在谈论的完整工作示例:https://stackblitz.com/edit/angular-2ukcqu
【问题讨论】:
【参考方案1】:如果您不想将所有内容添加到 DOM 中,您可以使用 forkJoin
(RxJS doc) 运算符对您的 observables 进行分组。然后你可以使用*ngIf
保持你的语法并且DOM 将保持干净。
解决方案 C
.ts
this.data$ = forkjoin([yourObs1, yourObs2,...]).pipe(
switchMap(myResolvedArray=>
// Here you must return the object you want
return
dataObs1: myResolvedArray[0]
...
)
)
.html
<div *ngIf='data$|async as data'>
//data.dataObs1 is accessible like other attributes
</div>
【讨论】:
【参考方案2】:在*ngIf
中使用async
管道。你可以在https://toddmotto.com/angular-ngif-async-pipe找到一篇很棒的文章
简而言之:
<div *ngIf="(user$ | async) as user; else loading">
<user-profile
[user]="user">
</user-profile>
<user-messages
[user]="user">
</user-messages>
</div>
<ng-template #loading>
Loading stuff...
</ng-template>
如果这对您不起作用,请使用另一种更清洁的解决方案 (IMO) BehaviorSubject。当您的 dom 开始订阅时,它将获得最后可观察到的数据。
【讨论】:
【参考方案3】:将*ngIf
重写为隐藏应该可以解决问题。 result$ 不起作用的原因是因为 *ngIf
中的那些元素在 itemLoading 为 false 之前不会被添加到 dom 中。那时他们将订阅 result$,但该事件已经发生了。
另外,shareReplay(1)
也可以做到这一点,而无需您重写任何其他内容,因为订阅 Observable 时也会运行回复。
解决方案 A
<div [hidden]="itemLoading">
<!-- ... -->
</div>
<div [hidden]="!itemLoading">
<p>LOADING...</p>
</div>
解决方案 B
this.result$ = this.searchTerm$.pipe(
//...
shareReplay(1)
);
【讨论】:
shareReplay 确实解决了这个问题。谢谢!以上是关于嵌套在 ngIf 中时,异步管道订阅无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章
如何在Angular 6中将异步管道的结果分配给没有*ngIf的变量
使用“* ngIf”的Component构造函数中的BehaviourSubject订阅会抛出“ExpressionChangedAfterItHasBeenCheckedError”