因此,我有一个方法依赖于两个可观察对象的数据,而我发现使它起作用的唯一方法是嵌套可观察对象的订阅,然后在第二个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();
}
答案 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);
}
}