我在项目中使用angular 10。我在ngOnInit函数中嵌套了HTTP调用,如下所示:
ngOnInit(): void {
let communetyid;
this.route.data.subscribe(data => {
this.route.params.subscribe(params => {
if(data.category === "code")
{
this.dataService.getCode(params.code)
.subscribe(resp => { communetyid = resp.results.communityId });
}
else
{
communetyid = params.id
}
this.dataService.getCommunityById(communetyid)
.subscribe(response => { this.community = response.results;
///other http calls
})
})
})
})
正如您在上面的代码中看到的,dataService.getCommunityById
获取参数communietyid,communietyide获取if语句中的值。由于异步dataService.getCommunityById
函数在communityid获取值之前触发,我在控制台中得到了错误。
我如何更改代码,即当dataService.getCommunityById
被激发时,值communietyid将被初始化。
我知道我可以在dataService.getCode
的订阅中复制dataService.getCommunityById
,但我想防止代码重复。
如果你想保持你使用的原始模式(嵌套订阅(,你应该只移动"getCommunityById";http调用到";dataService.getCode";回调,它就会工作。这里的关键点是要理解";dataService.getCode";将返回一个可观察的(不存在的数据(,而您的else分支使用已经存在的值(params.id(。解决这个问题的正确方法是在pipe((内部使用concatMap((并将这些操作链接起来。在concatMap((回调中,您将处理if-else分支逻辑,在那里您将返回this.dataService.getCode或Observable.of(params.id(,而在下一个concatMap((中您将使用此值作为this.dataServices.getCommunityById.的参数
您可以考虑使用更高阶的operators
链接您的Observables
export class ChildComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private dataService: DataService
) {}
// Extract Parameters from the Activated Route
params$ = this.route.paramMap.pipe(
map(params => ({
id: params.get("id"),
code: params.get("code"),
category: params.get("category")
}))
);
// Get Community Id
communityId$ = this.params$.pipe(
mergeMap(({ category, code, id }) =>
category === "code"
? this.dataService
.getCode(code)
.pipe(map(({ results }) => results.communityId))
: of(id)
)
);
// Get Community
community$ = this.communityId$.pipe(
switchMap((communetyId) => this.dataService.getCommunityById(communetyId))
)
ngOnInit() {
this.community$.subscribe({
next: console.log
});
}
}
代码说明
提取参数
params$ = this.route.paramMap.pipe(
map(params => ({
id: params.get("id"),
code: params.get("code"),
category: params.get("category")
}))
);
我使用paramMap
从ActivatedRoute
中提取参数。从Angular Docs来看,params
已被弃用(或将被弃用(
ActivatedRoute
包含两个性能不如其替代品的属性,并且可能在未来的Angular版本中被弃用。
下一步,我们定义一个属性communityId
,其值取决于通过检查category
属性映射params
。我正在使用mergeMap
运算符来合并params$
到communityId$
的订阅。
最后的步骤是得到community$
。这里我们使用switchMap
。如果有新的请求,我们不想继续最初的请求,因此这个运营商是这里最合适的
查看示例
将代码更改为以下内容:-
ngOnInit(): void {
let communetyid;
this.route.data.subscribe(data => {
this.route.params.subscribe(params => {
const communityIdObs = date.category === 'code' ? this.dataService.getCode(params.code).pipe(map(resp => resp.results.communityId)) : of(params.id);
communityIdObs.pipe(mergeMap(res => {
return this.dataService.getCommunityById(communetyid);
}).subscribe(response => {
this.community = response.results;
///other http calls
});
})
Mergemap操作员会按顺序调用,我也修改了代码,使其更短。
我认为您可以使用concatMap
操作员
要将一个响应转换为连续请求所需的参数,请使用concatMap
运算符concatMap()
在内部订阅从其投影函数返回的Observable,并等待直到它完成,同时重新发出其所有值。
以下是的示例
function mockHTTPRequest(url) {
return Observable.of(`Response from ${url}`)
.delay(1000);
}
let urls = ['url-1', 'url-2', 'url-3', 'url-4'];
let start = (new Date()).getTime();
Observable.from(urls)
.concatMap(url => mockHTTPRequest(url))
.timestamp()
.map(stamp => [stamp.timestamp - start, stamp.value])
.subscribe(val => console.log(val));
Event尽管您可以使用rxjs来链接可观测值。还有另一种简单的方法可以解决你的问题。如果你把任何东西放在可观察的订阅中,它就会被执行,所以你需要把它作为一个单独的函数移到外部。它应该可以帮助您防止调用dataService.getCommunityById函数。
ngOnInit(): void {
let communetyid;
this.route.data.subscribe(data => {
this.route.params.subscribe(params => {
if(data.category === "code")
{
this.dataService.getCode(params.code)
.subscribe((resp) =>
{
communetyid = resp.results.communityId;
getCommunityData(communetyid);
});
}
else
{
communetyid = params.id;
getCommunityData(communetyid);
}
})
})
})
getCommunityData(communetyid) {
this.dataService.getCommunityById(communetyid)
.subscribe(response => {
this.community = response.results;
//other http calls
})
}
这是使用rxjs的另一种方法,这更容易理解。您还可以使用rxjs运算符,如concatMap、的,如Medhat Mahmoud和Muhammet can TONBUL所建议的。