我有一个组件从服务获取数据:
ngOnInit(): void
{
this.categoryService.getCategories().subscribe((data) => {
this.categories=data;
}
)
}
这是服务代码:
getCategories(){
return this.category.find({where: {clientId: this.userApi.getCurrentId()}}).map((data) => {
return data;
}, err => {
console.log(err);
});
};
返回的数据结构是:
{"categoryName":"Google","id":"58591d2b7672d99910497bec","clientId":"585808f6737f6aad1985eab2"},{"categoryName":"Yahoo","id":"58591d4c7672d99910497bef","clientId":"585808f6737f6aad1985eab2"},{"categoryName":"Msn","id":"585d25f6ae4b2ecb056bc514","clientId":"585808f6737f6aad1985eab2"}
我在this.categories
上有另一种名为countLinks(id)
的方法; id
是一个类别参数,如585d25f6ae4b2ecb056bc514
,取自数据结构。
countLinks()
返回一个包含该特定类别的所有链接的observable。
我想在我的数据结构中添加另一个属性,其中包含每个类别ID的链接数量。 类似的东西:
{"categoryName":"Google","id":"58591d2b7672d99910497bec",**"nblinks":"0"**,"clientId":"585808f6737f6aad1985eab2"},
{"categoryName":"Yahoo","id":"58591d4c7672d99910497bef",**"nblinks":"5"**,"clientId":"585808f6737f6aad1985eab2"},
{"categoryName":"Msn","id":"585d25f6ae4b2ecb056bc514",**"nblinks":"12"**,"clientId":"585808f6737f6aad1985eab2"}
有什么想法吗?
答案 0 :(得分:2)
首先,你所做的是可怕的做法。以下是我如何解决您的问题:
如果要为两个呼叫打同一个后端,则需要编写一个端点来获取所需的所有数据。如果要打两个后端,则需要传递一个clientIds数组并在一次调用中获取链接数。如果您不控制第二个呼叫后端,那么很好,让我们按照步骤1和2中的大纲进行操作。
您的结构需要如下所示:
.forkJoin
Observable可以进行所有的个人通话 - 如果您熟悉的话,这就像Q.all一样。 我懒得为你编写完整的代码,抱歉,但基本上,当你获得数据时,需要创建一个Observable for EACH值,如下所示:
//This function will generate observables for you, when passed a param
function generateMockLinkCallObs(clientId){
var mockLinksCall = new Rx.Observable(observer => {
var mockCallTime = Math.floor(Math.random() * 5000) + 1000;
setTimeout(() => {
var mockLinks = Math.floor(Math.random() * 12) + 1;
observer.next(mockLinks);
}, mockCallTime)
})
return mockLinksCall;
}
//This just uses the function above to generate your observables for EACH object
generateListOfObservableCalls(ids){
var obsArray = [];
var obs;
ids.forEach(id => {
obs = generateMockLinkCallObs(id);
obsArray.push(obs);
})
return obsArray;
}
//This just uses the above function.
getNumberOfLinks(data){
var ids = data.map(data => data.id)
var observables = generateListOfObservableCalls(ids);
return observables;
}
所以,总而言之,你得到了你的初始数据:
httpWhatever.subscribe(
next => {
var observablesForIndividualCalls = getNumberOfLinks(next);
Observable.forkJoin(observablesForIndividualCalls)
.subscribe((response) => {
console.log(response[0], response[1]);
});
}
)
我过度简化了最后一步,但forkJoin只会在您获得所有个人通话的结果后才能完成。
不,我不知道更简单的方法,尽管它绝对可以更清洁。
答案 1 :(得分:2)
您所描述的是mergeMap()运营商的典型用例。
试试这段代码:
getCategories() {
return this.category.find({where: {clientId: ID}})
.mergeMap(category =>
countLinks(category.id).map(categoryLinks => {
return Object.assign({}, category, { nblinks: categoryLinks.length })
})
);
};
我将第一个observable(由this.category.find()
返回)映射到另一个observable(由countLinks()
返回)。
在最里面的map()
中,我最终同时使用category
和categoryLinks
,并将它们合并为Object.assign()
的单个对象。
旁注:您的原始代码有点令人困惑。你写了#34;我在this.categories上有另一个方法叫做countLinks(id)"但你也写了this.categories=data
。你如何在数据结构上有一个方法?我在假设this.category.find()
和countLinks()
都返回可观察量的情况下写了我的答案,但是你必须根据你的具体情况调整我的解决方案代码/状况。
答案 2 :(得分:0)
创建两个接口。一个代表类别的基础,另一个代表它。
interface ICategory {
categoryName: string;
id: string;
clientId: string;
}
interface ICategoryExpanded extends ICategory {
nbLinks: string;
}
然后使用RxJS中的.map()运算符将数据转换为第二个接口。
this.getCategories().map((cats: ICategory[]) => {
let mapCategories: ICategoryExpanded[] = [];
cats.forEach((cat: ICategory) => {
let transformCat: ICategoryExpanded = <ICategoryExpanded>cat;
transformCat.nbLinks = this.countLinks(transformCat.id);
mapCategories = [...mapCategories, transformCat];
});
return mapCategories;
})
.subscribe((returnCategoriesExt) => this.categories = returnCategoriesExt);
那应该处理你想要的东西并返回这样的东西:
[{ "categoryName": "Google", "id": "58591d2b7672d99910497bec", "clientId": "585808f6737f6aad1985eab2", "nbLinks": "5" },
{ "categoryName": "Yahoo", "id": "58591d4c7672d99910497bef", "clientId": "585808f6737f6aad1985eab2", "nbLinks": "5" },
{ "categoryName": "Msn", "id": "585d25f6ae4b2ecb056bc514", "clientId": "585808f6737f6aad1985eab2", "nbLinks": "5" }]