Rxjs init资源一次使用热可观察

时间:2017-11-13 00:25:57

标签: angular typescript rxjs observable system.reactive

我的服务设置如下:

@Injectable()
export class StaticResourcesService {

private resourcesTree$: Observable<any>;

getSpecificLabel(resourceName: string, type: string): Observable<string> {
    return this.getResources().map(resources => {
      return jp.value(resources, `$.${resourceName}[?(@.type=="${type}")].label`);
    });
  }

private getResources(): Observable<any> {
    if (this.resourcesTree$ != null) {
      return this.resourcesTree$;
    }
    let source = this.getResourcesFilePath() // get obs<string> with a file path
      .mergeMap(path => {
        console.log(`static-resources path: ${path}`);
        return this.http.get(path).map(res => res.json()); // get json obj
      }).do(res => {
        this.resourcesTree$ = Observable.of(res);
      });
    return source;
  }

一个自定义角度管道,其转换方法如下:

transform(resourceType: string, resourceName: string): Observable<string> {
    return this.staticResourcesService.getSpecificLabel(resourceName, resourceType);
  }

我在一个简单的html页面中使用这个自定义管道:

<div *ngFor="let item of items">{{ item?.status | staticResource:'MY_RES' | async }}</div>

当我第一次加载页面时,自定义管道将处理4次(ngFor循环4x)并且永远不会从resourcesTree $获取其源数据。我理解这是一种正常的行为,因为订阅会同时收听,并且会执行4次。但我想要的是一种优化的方式,只需一次访问数据,然后在接下来的3次利用resourcesTree $。我试图通过在源上使用share()方法使其“热”。但我不能让它正常工作。

有人可以告诉我如何实现这一目标吗?也许我错了,我真的愿意接受这里的任何建议。

谢谢

1 个答案:

答案 0 :(得分:1)

异步服务的通常模式是使共享资源成为Subject()或ReplaySubject()。这样,你只需连接订阅者,他们就可以很好地等待数据到达。

在您的示例中,它正在切割资源,因此这是一个地图运算符(就像您已经拥有的那样)。另外一些发出提取信号的方法是 启动 (不管它是否已经完成,因为它是可观察的)。

由于getResources()不再在外部订阅,因此您需要将.do()替换为.subscribe()。

@Injectable()
export class StaticResourcesService {

private resourcesTree$ = new ReplaySubject(1);
private status = 'unloaded';

getSpecificLabel(resourceName: string, type: string): Observable<string> {
  if( this.status === 'unloaded') {
    this.status = 'loading';
    getResources();   
  }
  return resourcesTree$.map(resources => {
    return jp.value(resources, `$.${resourceName}[?(@.type=="${type}")].label`);
  });
}

private getResources(): Observable<any> {
  this.getResourcesFilePath() // get obs<string> with a file path
    .mergeMap(path => {
      console.log(`static-resources path: ${path}`);
      return this.http.get(path).map(res => res.json()); // get json obj
    })
    .subscribe(res => {
        this.resourcesTree$.next(res);
        this.status = 'loaded';
      });
  }