来自多个HTTP请求的RxJ / Angular加载以及每个请求的更新模型

时间:2019-02-02 23:10:12

标签: javascript angular rxjs

我已经使用Angular / RxJS了几周了,并且具有基于多个REST请求构建的各种模型,到目前为止,我已经使用switchMap()实现了该模型。这是一个简单的示例(stackblitz:https://stackblitz.com/edit/angular-o1djbb):

import { Component, OnInit, } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay, switchMap } from 'rxjs/operators';

interface Order {
  id: string;
  itemName: string;
  details?: string;
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  order: Order;

  ngOnInit() {
    this.getOrderFromApi(123)
      .subscribe(item => this.order = item);
  }

  getOrderFromApi(id): Observable<Order>  {
    const item$ = this.getItemName(id);
    const fullItem$ = item$.pipe(
      switchMap(n => {
        console.log(`Got name: '${n}''`);
        return this.getDetails(n);
      },
        (nameReq, detailsReq) => ({ id: '123', itemName: nameReq, details: detailsReq })
      ));
    return fullItem$;
  }

  getItemName(id): Observable<string> {
    return this.fakeXhr('foo');
  }

  getDetails(itemName): Observable<string> {
    console.log(`Got details '${itemName}''`)
    return this.fakeXhr('Some details about foo');
  }

  fakeXhr(payload: any) {
    return of(payload)
      .pipe(delay(2000));
  }
}

还有一个简单的模板:

<p>
  item: {{order && order.itemName}}
</p>
<p>
  details: {{order && order.details}}
</p>

这有效,但是在完成两者请求之前,订单信息不会呈现。我想让 itemName 尽快呈现,然后在细节可用时呈现细节。因此,利用了Observables可以发出的多个值。例如:

// first value emitted:
{ itemName: 'foo', details: null }
// second value emitted:
{ itemName: 'foo', details: 'Some details about foo' }

我意识到我可能可以通过BehaviourSubject或Redux来实现(就像我过去使用React一样),但是由于所有这些对我来说都是新事物,所以我觉得我没有一个简单的解决方案。

4 个答案:

答案 0 :(得分:2)

使用expand直接从第一个请求发出数据,然后执行第二个请求。

expand将递归调用内部请求,从上一个请求中获取order作为输入,因此我们仅在order没有details时执行第二个请求并以EMPTY Observable结束递归。

import { Observable, EMPTY } from 'rxjs';
import { map, expand } from 'rxjs/operators';

getOrderFromApi(id): Observable<Order> {
  return this.getItemName(id).pipe(
    map(itemName => ({ id, itemName, details: null } as Order)),
    expand(order => order.details
      ? EMPTY
      : this.getDetails(order.itemName).pipe(
        map(details => ({ id, itemName: order.itemName, details } as Order))
      )
    )
  );
}

https://stackblitz.com/edit/angular-umqyem

返回的Observable将发出:

  1. { "id": 123, "itemName": "foo", "details": null }
  2. { "id": 123, "itemName": "foo", "details": "Some details about foo" }

答案 1 :(得分:0)

也许最好的选择是用case playeragain = 11 (binary 1011) case player = 1 (Binary 0001) case GBullet = 2. (Binary 0010) case BBullet = 3. (Binary 0011) case enemyships = 4. (Binary. 0100) case coin = 5. (Binary 0101) case boss = 6. (Binary 0110) 代替mergeMap()

  

https://blog.angular-university.io/rxjs-higher-order-mapping/

     

如我们所见,合并的源Observable的值显示出来   立即输出。结果“可观察”将不会完成   直到所有合并的Observables完成。

我一定会看一下这些文章:

答案 2 :(得分:0)

您可以将接收到的值存储在代码的各个部分中,即在它们可用之后,而不是将整个项目保留在可观察流的末尾(即,在.subscribe块中)

 switchMap(n => {
        console.log(`Got name: '${n}''`);
        this.order.name = n;
        return this.getDetails(n);
      },

...,然后在sibscription块中,您只需存储高级信息:

this.getOrderFromApi(123)
      .subscribe(item => this.order.details = item.details);

很显然,这要求您相应地初始化“ order”属性,例如与order: Order = {};order: Order = new Order();

基本上,这个想法是重组您的代码。您的可观察流将发出部分值(您订单的属性),而不是完整的对象。

答案 3 :(得分:0)

我想出的一个解决方案是只创建一个Observable并调用next()。我很想听听对此的任何看法。

getOrder(id) {
    const ob$ = new Observable(subscriber => {
      const item$ = this.getItem(id)
        .pipe(
          tap(o => subscriber.next(({ id: id, itemName: o.itemName, details: null }))),
          switchMap(o => this.getDetails(o.itemName),
            (o, d) => ({ id: id, itemName: o.itemName, details: d.details })
          ),
          tap(a => subscriber.next(a))
        );
      item$.subscribe(a => {
        subscriber.complete();
      });
    });

    return ob$;
  }

Stackblitz:https://stackblitz.com/edit/angular-mybvli