【问题标题】:Async pipe subscription not work correctly when nested inside ngIf嵌套在 ngIf 中时,异步管道订阅无法正常工作
【发布时间】:2019-01-29 03:48:00
【问题描述】:

我有一个快速搜索框,我想要一个加载动画。我使用ng-templatengIf 来显示/隐藏这个动画。我有一些 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

【问题讨论】:

    标签: javascript angular typescript rxjs angular6


    【解决方案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 确实解决了这个问题。谢谢!
        猜你喜欢
        • 2016-06-24
        • 2021-06-09
        • 2020-04-12
        • 2016-07-26
        • 2018-02-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-06
        • 2011-09-01
        相关资源
        最近更新 更多