【问题标题】:Angular Subscribe within Subscribe订阅中的角度订阅
【发布时间】:2019-04-01 03:35:28
【问题描述】:

我有以下包含多个订阅的代码。我需要实现的是这样的:

  1. 订阅激活路由以获取用户和产品数据。
  2. 返回商品数据后,使用商品数据订阅getSeller服务。
  3. 使用返回的卖家数据订阅getRating服务。

我的问题:有没有更好的方法来执行这些嵌套订阅?这样做是个好习惯吗?

this.activatedRoute.data.pipe(
 map((data) => {
    this.user = data['user'];
    this.product = data['product'];

    return this.product;
  })
).subscribe(result => {

  if (this.product === null) {
    this.router.navigate(['/home']);
  } else {
    this.displayCurrency = this.dataService.getCurrencySymbolById(this.product.currency);

    this.userService.getUser(this.product.createdBy).subscribe(seller => {
      this.seller = seller;

      this.ratingService.getRatingByUserId(seller.id).subscribe(rating => {
        this.rating = rating;
      })

    });
  }
});

【问题讨论】:

  • 在此之前,请记住这一点。您必须在不需要时取消订阅。在这种情况下,产品创建和卖家 ID 订阅。现在你原来的问题是,你看过 rxjs 操作符吗?如果没有,请通过它们。你的问题可能会很容易被他们解决。

标签: angular rxjs


【解决方案1】:

从技术上讲,嵌套订阅是可行的,但有一种更优雅、更系统的方式来处理这个问题。你真的应该更多地了解你的 RxJS 操作符。

首先,我们使用mergeMap 将来自activatedRoute 的可观察值映射到内部可观察值。

然后,我们使用 forkJoin 将 observable 组合成单个值 observable,从而在 .subscribe() 上返回值本身

this.activatedRoute.pipe(
    tap(data => console.log(data)),
    mergeMap(data => {
      if (data.product === null) {
        this.router.navigate(['/home']);
      } else {
        const getCurrency = this.dataService.getCurrencySymbolById(data.product.currency);
        const getUsers= this.userService.getUser(data.product.createdBy);
        const getRatings = this.ratingService.getRatingByUserId(seller.id)
        return forkJoin(getCurrency, getUsers, getRatings);
      }
    })
  ).subscribe(res => {
    console.log(res[0]); // currency
    console.log(res[1]); // user
    console.log(res[2]); // ratings

  }

编辑:原来我误读了原始问题,因为 getRatingsByUserId 依赖于 getUser。让我做一些改变。无论哪种方式,我都会保留上面的代码,因为它有利于 OP 的参考。

this.activatedRoute.data.pipe(
  switchMap(data => {
    this.user = data['user'];
    this.product = data['product'];
    return this.userService.getUser(this.product.createdBy);
  }),
  switchMap(data => {
    if (this.product === null) {
      this.router.navigate(['/home']);
    } else {
      this.seller = seller;
      return this.userService.getRatingByUserId(this.product.createdBy); 
    }
  })
).subscribe(res => {
 console.log(res)
 // handle the rest
})

【讨论】:

  • 如果我没看错问题,seller 被读取为以product.createdBy 作为参数调用getUser。这意味着getUser 应该通过switchMapgetRatingsByUserId 连接起来
  • 哦,对了,谢谢你指出这一点..!我看错了那部分
【解决方案2】:

使用 switchMap 切换到一个新的 observable。

this.activatedRoute.data.switchMap((routeData) => {
     this.user = routeData['user'];
     this.product = routeData['product'];
     return this.userService.getUser(this.product.createdBy);
    }).switchMap(seller => {
       this.seller = seller;
        return this.ratingService.getRatingByUserId(seller.id);
    }).subscribe(rating => {
      this.rating = rating;
    })

【讨论】:

    【解决方案3】:

    您可以使用mergeMap

    import { of, from } from 'rxjs';
    import { map, mergeMap, delay, mergeAll } from 'rxjs/operators';
    
    const getData = (param) => {
        return of(`retrieved new data with param ${param}`).pipe(
            delay(1000)
        );
    };
    
    
    from([1, 2, 3, 4]).pipe(
        mergeMap(param => getData(param))
    ).subscribe(val => console.log(val));
    

    【讨论】: