摆脱嵌套的可观察物

时间:2019-12-06 13:50:39

标签: angular rxjs

因此,我有一个方法依赖于两个可观察对象的数据,而我发现使它起作用的唯一方法是嵌套可观察对象的订阅,然后在第二个subscription方法中调用该方法。这似乎很糟糕,必须有一种更好的方法来完成此操作,我可以寻求一些更好的方法来帮助您完成此操作吗?

这是控制器:

meta$: Subscription;
meta: any;
data$: Subscription;
data: any;

ngOnInit() {
  this.route.params.subscribe(params => this.id = params['id']);
  this.getPageInfo();
}

private getPageInfo(): void {
  this.meta$ = this.api.get(`${Endpoints.LOCATIONS}all/metadata`).subscribe(m => {
    this.meta = m;
    this.data$ = this.api.get(`${Endpoints.LOCATIONS}/${this.id}`).subscribe(d => {
      this.data = d;
      this.setSelectedAttributes(); // <-- relies on this.meta and this.data
    }, err => console.error(err));
  }, err => console.error(err));
}

setSelectedAttributes(): void {
  const viewableAttributes = this.meta.columns.filter(c => !c.hidden && c.type === 'String');
  const selectedAttributes: Array<any> = [];
  for (const attr of viewableAttributes) {
    if (this.data[attr.field]) {
      selectedAttributes.push({name: attr.title, value: this.data[attr.field]});
    }
  }
  this.selectedAttributes = selectedAttributes;
}

ngOnDestroy() {
  this.data$.unsubscribe();
  this.meta$.unsubscribe();
}

4 个答案:

答案 0 :(得分:2)

您似乎可以只使用concatMap,然后映射第二个响应以包含第一个响应的响应,也许您甚至不需要使用任何属性:

this.api.get(`${Endpoints.LOCATIONS}all/metadata`).pipe(
  concatMap(response1 => this.api.get(`${Endpoints.LOCATIONS}/${this.id}`)).pipe(
    map(response2 => [response1, response2])
  )
).subscribe(([response1, response2]) => {
  // Whatever here. You can set `this.meta = response1` etc...
});

答案 1 :(得分:0)

看起来这两个api调用可以并行发生,请使用forkJoin()tap()产生副作用->实例属性分配

private getPageInfo(): void {
 forkJoin(this.api.get(`${Endpoints.LOCATIONS}all/metadata`),this.api.get(`${Endpoints.LOCATIONS}/${this.id}`)).pipe(tap(([m,d])=>{
this.data$=d
this.meta$=m
this.setSelectedAttributes()
}))
}

答案 2 :(得分:0)

没有任何订阅,您可以这样做:

id$: Observable<number> = this.route.params.pipe(
    map(params => +params.id),
);


data$: Observable<Data> = this.id$.pipe(
  switchMap(id => this.api.get(`${Endpoints.LOCATIONS}/${this.id}`)),
);

export interface MetaData {
  columns: Atribute[];
}

export interface Attribute {
  hidden: boolean;
  type: string;
  title: string;
  field: string;
}

viewableAttributes$: Observable<Attribute[]> = this.api.get<MetaData>(`${Endpoints.LOCATIONS}all/metadata`).pipe(
  map(meta => meta.columns),
  filter(column => !column.hidden && c.type === 'String'),
);


export interface AttributeSelection {
  name: string;
  value: string;
}

selectedAttributes$: Observable<AttributeSelection[]> = forkJoin([this.data$, this.viewableAttributes$]).pipe(
  map(([data, viewableAttributes]) => viewableAttributes.map(attr => ({ name: attr.title, value: data[attr.field]})).filter(attr => attr.value)),
);

您只需要在selectedAttributes$上使用异步管道

答案 3 :(得分:-1)

您可以使用flatMap()。这与用于阻止嵌套订阅的mergeMap()相同:

this.meta$ = this.api.get(...).map(
   flatMap((m) => {
      this.meta = m;
      ...
      return this.api.get(...)

   }),
   // You could also make another request if you need
   // flatMap((m) => {
   //    ...
   //    return this.api.get(...)
   // }),
).subscribe((d) => {
   this.data = d;
});

有关更多信息:https://www.learnrxjs.io/operators/transformation/mergemap.html

另一个提示:

如果要销毁1个变量中的所有内容,请使用takeUntil()绑定到销毁属性:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  destroy$ = new Subject<boolean>();

  request() {
     return this.apiService.get(...).pipe(
         takeUntil(this.destroy$)
     )
  }

  ngOnDestroy(): void {
    this.destroy$.next(true);
  }
}