如何使用angular2链接rxjs Observable操作?

时间:2017-01-31 15:27:57

标签: angular rxjs observable

我有一个组件从服务获取数据:

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"}

要添加信息,类别是数组类型: Preview data structure with Augury

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

首先,你所做的是可怕的做法。以下是我如何解决您的问题:

  1. 从后端获取对象列表。
  2. 从后端获取每个对象的附加属性,每个对象都有一个调用。
  3. 如果要为两个呼叫打同一个后端,则需要编写一个端点来获取所需的所有数据。如果要打两个后端,则需要传递一个clientIds数组并在一次调用中获取链接数。如果您不控制第二个呼叫后端,那么很好,让我们按照步骤1和2中的大纲进行操作。

    您的结构需要如下所示:

    • 可观察获取数据(你有这个)。一个将此映射到ID的函数。
    • 从每个收到的Id
    • 创建可观察对象的函数
    • 一个.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()中,我最终同时使用categorycategoryLinks,并将它们合并为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" }]