【问题标题】:Angular RxJS: event emitted twiceAngular RxJS:事件发出两次
【发布时间】:2019-05-28 11:30:24
【问题描述】:

我编写了两个 observables:

一个用于捕获表单提交事件:

<form role="form" (ngSubmit)="search()">
   ...
</form>

到达search() 并发出:

private search(): void {
    this.searchClickSubject.next();
}

在组件构造函数中:

private searchClickSubject:Subject<void>;
private searchClick$:Observable<any>;

constructor() {
    this.searchClickSubject = new Subject<void>();
    this.searchClick$ = this.searchClickSubject.asObservable();
}

如您所见,我在 searchClick$ 上使用 searchClickSubject.asObservable 方法从主题 searchClickSubject 创建了一个 Observable

从现在开始,我使用这个searchClick$ Observable 来捕捉submit form

之后,我正在创建另一个 Observable:

    this.searchQuery$ = this.searchClick$.pipe(
        map(() => <Query>{
            offset: 0,
            limit: 10
        })
    );

因此,每次发出 Query 时,我都会使用自定义管道函数发出请求:

    this.metrics$ = this.searchQuery$
        .pipe(
          pageLoading(),
          makeRequest(),
          loadedPage()
        );

这是我的ngOnInit()

public ngOnInit() {            
    // Grab search button click event
    this.searchQuery$ = this.searchClick$.pipe(
        map(() => <Query>{
            offset: 0,
            limit: 10
        })
    );

    this.metrics$ = this.searchQuery$
        .pipe(
          //...
        );
}

但是,请求被启动了两次。

我使用async 管道创建订阅:

<div class="row" *ngIf="!(metrics$ | async)" style="margin: 1.54em;">

<div *ngIf="metrics$ | async; let metric;"...

有什么想法吗?

【问题讨论】:

  • 你在哪里订阅metrics$

标签: angular typescript rxjs


【解决方案1】:

您订阅了 observable metrics$ 两次。这是发生了什么:

  1. 您使用*ngIf="!(metrics$ | async)"订阅。
  2. 当您单击搜索时,它会立即触发 observable,以及您在其上链接的所有内容,包括您的请求。
  3. 您使用*ngIf="metrics$ | async; let metric;"订阅。
  4. 第 2 步再次单独发生。

metrics$ 中使用share() 运算符来防止对新订阅重新发出请求。如果你想对这个话题有更深入的了解,可以看看这个great article

【讨论】:

  • 你为什么说我使用的是BehaviorSubject?据我所知,我正在使用Subject&lt;void&gt; 作为初始源来捕获表单提交...另一方面... ReplaySubject 怎么样?我认为分享和http请求不是一个好主意...
  • 所以,我的意思是,share() 运算符是一个很好的解决方法,但我认为它闻起来像是一种“解决方法”。根据 ot 和冷问题:首先 observable 是热的(表单提交),然后在 switchMap 之后变成冷的 observable,不是吗?
  • 我不认为在这种情况下这是一种解决方法。您有一个组件,并且在该组件中,您希望在两个订阅之间共享​​>相同的 observable。你是对的,你不使用BehaviorSubject,我的错,对不起。不过情况是一样的。
【解决方案2】:

您已订阅metrics$ 两次,您可以使用as 语法代替,如下所示

<div *ngIf="metrics$ | async as metric; else loading">
   <!-- Statements when metrics data found  -->
</div>

<ng-template #loading>
   <!-- Loading stuff...  -->
  <div class="row" style="margin: 1.54em;">
</ng-template>

更多详情请参考Handling observables with NgIf & Async Pipe

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-14
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 2018-02-03
    • 2014-04-07
    相关资源
    最近更新 更多