Angular 摆脱嵌套的观测值

Angular 摆脱嵌套的观测值,angular,rxjs,Angular,Rxjs,因此,我有一个方法,它依赖于来自两个观察值的数据,我发现让它工作的唯一方法是嵌套观察值的订阅,然后在第二个订阅方法中调用该方法。这似乎很糟糕,必须有更好的方法来实现这一点。我可以得到一些帮助,以更好的方式做到这一点吗 这是控制器: meta$: Subscription; meta: any; data$: Subscription; data: any; ngOnInit() { this.route.params.subscribe(params => this.id = par

因此,我有一个方法,它依赖于来自两个观察值的数据,我发现让它工作的唯一方法是嵌套观察值的订阅,然后在第二个订阅方法中调用该方法。这似乎很糟糕,必须有更好的方法来实现这一点。我可以得到一些帮助,以更好的方式做到这一点吗

这是控制器:

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();
}
meta$:订阅;
梅塔:任何;
数据$:订阅;
资料:有;
恩戈尼尼特(){
this.route.params.subscribe(params=>this.id=params['id']);
这是getPageInfo();
}
私有getPageInfo():void{
this.meta$=this.api.get(`${Endpoints.LOCATIONS}all/metadata`)。订阅(m=>{
this.meta=m;
this.data$=this.api.get(`${Endpoints.LOCATIONS}/${this.id}`)。订阅(d=>{
这个数据=d;
this.setSelectedAttribute();//console.error(err));
},err=>console.error(err));
}
SetSelectedAttribute():void{
const viewableAttributes=this.meta.columns.filter(c=>!c.hidden&&c.type=='String');
常量SelectedAttribute:Array=[];
for(可视属性的常量属性){
if(此.data[attr.field]){
selectedAttributes.push({name:attr.title,value:this.data[attr.field]});
}
}
this.selectedAttributes=selectedAttributes;
}
恩贡德斯特罗(){
此.data$.unsubscribe();
此.meta$.unsubscribe();
}

看起来这两个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()
}))
}
您可以使用
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;
});
有关更多信息:

另一个提示:

如果要销毁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);
  }
}


@组件({
选择器:“我的应用程序”,
templateUrl:“./app.component.html”,
样式URL:['./app.component.css']
})
导出类AppComponent{
销毁$=新主题();
请求(){
返回此.apiService.get(…).pipe(
takeUntil(此.destroy$)
)
}
ngOnDestroy():void{
this.destroy$next(true);
}
}

看起来您可以只使用
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...
});

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

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)),
);
id$:Observable=this.route.params.pipe(
映射(参数=>+参数id),
);
data$:Observable=此.id$.pipe(
switchMap(id=>this.api.get(`${Endpoints.LOCATIONS}/${this.id}'),
);
导出接口元数据{
柱:阿曲布他[];
}
导出接口属性{
隐藏:布尔;
类型:字符串;
标题:字符串;
字段:字符串;
}
viewableAttributes$:Observable=this.api.get(`${Endpoints.LOCATIONS}all/metadata`).pipe(
映射(meta=>meta.columns),
筛选器(column=>!column.hidden&&c.type==='String'),
);
导出接口属性选择{
名称:字符串;
值:字符串;
}
selectedAttributes$:Observable=forkJoin([this.data$,this.viewableAttributes$]).pipe(
map([data,viewableAttributes])=>viewableAttributes.map(attr=>({name:attr.title,value:data[attr.field]})).filter(attr=>attr.value)),
);

然后,您只需在
selectedAttribute$

上使用异步管道,不确定此方法是否有效,但您可以在范围内定义一组订阅,用您的所有订阅填充它,然后在
ngondestory
中检查订阅是否处于活动状态,如果是
取消订阅
@Bargros那么我的问题就解决了不取消订阅这是我必须首先嵌套它们的事实,这是我试图避免的:)使用
pipe
,就像这样:
this.api.get('..').pipe(merge(getResult=>this.api.get('..')).subscribe(result=>//dose)
;为什么要在concat映射看起来是独立的时候使用它们呢?forkJoin似乎是首选的操作符,因为它是在parralel中执行的,除非您的目标是不在parralel中同时发送它们,即使这样,concat->reduce在我看来还是一个更干净、更可扩展的结构