RxJS共享运算符* ngIf内部存在问题

时间:2019-11-30 19:35:05

标签: angular rxjs

我开始对RxJS运算符有一定的了解,但是我仍然对其中的一些困难。在实现搜索组件时,我具有以下代码:

      searchResult$: Observable<LunrDoc []>;
      searchResultLength$: Observable<number>;

      ngAfterViewInit(): void {

        // observable to produce an array of search hits

        this.searchResult$ = fromEvent<Event>(this.searchInput.nativeElement, 'keyup').pipe(
          debounceTime(1000),
          switchMap(e => this.ss.search((e.target as HTMLTextAreaElement).value)),
          share()
        );

        // observable to return the length of the array

        this.searchResultLength$ = this.searchResult$.pipe(
          map(sr => sr ? sr.length : 0),
          share()
        );
      }

这在模板中使用如下:

    <p *ngIf="(searchResultLength$ | async) > 0">
      Total Documents: {{ (searchResultLength$ | async) | number }}
    </p>

    <p *ngFor="let doc of (searchResult$ | async)">
      <span *ngIf="doc.path" [routerLink]="doc.path" style="color: darkblue; font-weight: bold; text-underline: darkblue; cursor: pointer">
        {{ doc.title }}
      </span>
      {{ doc.content.substring(0, 400) }}
    </p>

发生的情况是,当searchResult$发出非null数组时,可观察到的第一段元素中的呈现结果为“ Total Documents:”,其后没有数字。 * ngFor 装饰段落完全符合预期。

我相信,原因是第二个async管道被激活,并在发出最后一个值后订阅了共享的observable。因此,它永远不会收到“下一个”呼叫。

是否有RxJS运算符代替share来解决此问题?还是我想念其他东西?

1 个答案:

答案 0 :(得分:1)

如@IngoBürk所建议。 shareReplay(1)可能就是您想要的。 1表示缓冲区大小(应播放多少个值)。

Share replay docs

我还将建议避免创建多个可观察对象。

例如这将创建两个不同的订户。

<p *ngIf="(searchResultLength$ | async) > 0">
  Total Documents: {{ (searchResultLength$ | async) | number }}
</p>

替代方法是:

<p *ngIf="(searchResultLength$ | async) as searchResultLength > 0">
  Total Documents: {{ searchResultLength | number }}
</p>

建议的解决方案

通过查看代码并尝试了解其功能,我认为您可以进行这些更改以获得所需的输出。

组件:

searchResult$: Observable<LunrDoc []>;

ngAfterViewInit(): void {
    this.searchResult$ = fromEvent<Event>(this.searchInput.nativeElement, 'keyup')
    .pipe(
        debounceTime(1000),
        switchMap(e => this.ss.search((e.target as HTMLTextAreaElement).value)),
    );
}

您不需要shareshareReplay,因为它们似乎没有提供模板所需的任何功能。除非模板中还有其他代码在您的问题中不可见。

可观察到的this.searchResultLength$似乎也很多余,因为它只是length返回的值的this.searchResult$属性。

模板:

<ng-container *ngIf="{searchResult: searchResult$ | async} as vm">
    <p *ngIf="vm.searchResult.length > 0">
        Total Documents: {{ vm.searchResult }}
    </p>

    <p *ngFor="let doc of vm.searchResult">
        <span *ngIf="doc.path" [routerLink]="doc.path" style="color: darkblue; font-weight: bold; text-underline: darkblue; cursor: pointer">
            {{ doc.title }}
        </span>
        {{ doc.content.substring(0, 400) }}
    </p>
</ng-container>

包装器ng-container将始终显示,因为ngIf条件的计算结果为真实值。

注意

在处理触发可观察的输入时,请考虑使用ditinctUntilChanged运算符:https://www.learnrxjs.io/operators/filtering/distinctuntilchanged.html