嵌套在ngIf中时,异步管道订阅无法正常工作

时间:2018-08-22 20:08:08

标签: javascript angular typescript rxjs angular6

我有一个快速搜索框,我想要一个加载动画。我使用ng-templatengIf来显示/隐藏此动画。而且我在同一div内嵌套了一些li,该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;
          })
      ))
    );
  }

当父div中有ngIf时,“源已到达”消息永远不会记录在控制台中,并且加载模板也一直挂在那里。

以下是我所谈论的内容的完整示例:https://stackblitz.com/edit/angular-2ukcqu

3 个答案:

答案 0 :(得分:1)

*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)
);

答案 1 :(得分:0)

async中使用*ngIf管道。您可以在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订阅时,它将获得最新的可观察数据。

答案 2 :(得分:0)

如果您不想将所有内容都添加到您的 DOM 中,您可以使用 forkJoin (RxJS doc) 运算符对您的可观察对象进行分组。然后您可以使用 *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>