Angular2 + RxJS嵌套HTTP调用

时间:2017-03-16 08:57:19

标签: angular typescript rxjs

大家早上好。我是Angular2的新手,在我正在研究的项目中,我必须发出一些HTTP请求才能从REST后端检索数据。

从后端我必须得到一个模块列表。每个模块都有一个或多个操作。表示两个实体的typescript类如下

export class Module {

    public name: string;
    public htmlPath: string;
    public actions: ModuleAction[];

    constructor(data: IModule){
        this.name = data.name;
        this.actions = [];    
        this.htmlPath = data.htmlPath;
    }

    addAction(action: ModuleAction): void {
        this.actions.push(action);
    }
}

export class ModuleAction {
    private name: string;

    constructor(data: IModuleAction){
        this.name = data.actionName;
    }
}

支持公开调用以检索没有相关操作的模块列表,并公开第二个端点以检索给定模块的操作列表。为了初始化我的角度接口,我必须发出第一个HTTP请求来获取模块列表。一旦获得模块,我必须为每个模块发出第二个请求,以检索操作并更新Module实例。

昨天我尝试使用RxJS来获取数据。我找到的唯一可行的解​​决方案是以下

getModules(type?: string) : Observable<Module[]> {
    let url = ...
    console.log("getting modules");
    return this.http.get(url)
        .map(res => {
          let body = res.json();
          let modules = body.map(jsModule => {
            let m = new Module(jsModule);
            let url = HttpHandlerService.URL_INFO + '/' + m.htmlPath;

            console.log("getting actions for module " + m.name);
            this.http.get(url)
                .map(response => {
                  let body = response.json();
                  return body.actions.map(function(a : IModuleAction){
                    return new ModuleAction(a);
                  });
                })
                .subscribe(actions => {
                  for(let a of actions)
                    m.addAction(a);
                });
            return m;
          });
          return modules;
        })
        .catch(this.handleError);
}

我很确定我错误地使用了RxJS运算符,并且必须有一个不同的解决方案才能以更“符合RxJS”的方式获得相同的结果。

如何使用RxJS运算符来获得相同的结果? 非常感谢你的帮助。

3 个答案:

答案 0 :(得分:3)

使用Observable.flatMap运算符可以实现链接相互依赖的HTTP请求。请参阅此帖子:Angular 2 - Chaining http requests

而且,由于您需要检索每个模块的模块操作,请使用Observable.forkJoin运算符。请参阅此帖子:Send multiple HTTP GET requests in parallel

最终结构如下所示:

getModules()
    .flatMap(modules => {
        this.modules = modules;
        let moduleActionsObservables = modules.map(module => {
            return this.getModuleActions(module);
        });

        return Observable.forkJoin(moduleActionsObservables);
    })
    .map(result) => {
        // module actions for each module
        console.log(result); // => [[ModuleAction, ModuleAction], [ModuleAction], [ModuleAction, ModuleAction]]

       // if you want to assign them to their respective module, then:
       this.modules.forEach((module, i) => {
          module.actions = result[i];
       });

       return this.modules;
    });


// The result:
this.getModules().subscribe(
    modules => {
        console.log(modules); // => [{moduleId: '123', actions: [..]}, {moduleId: '124', actions: [..]}]
    });

答案 1 :(得分:0)

尝试进行一些分色并使用switchMap:

getModules(type?: string) : Observable<Module[]> {
    let url = ...
    console.log("getting modules");
    return this.http.get(url)
        .switchMap(res => {
          let body = res.json();
          return getJsModules(body);
        })
        .catch(this.handleError);
}



 getJsModules(body){
     return body.switchMap(jsModule => {
            let m = new Module(jsModule);
            let url = HttpHandlerService.URL_INFO + '/' + m.htmlPath;

            console.log("getting actions for module " + m.name);

           return getModuleActions.map(actions => {
                    for(let a of actions)
                      m.addAction(a);
                  });
          });
 }


getModuleActions(url){
    return this.http.get()
       .switchMap(response => {
           let body = response.json();
           return body.actions.map(function(a : IModuleAction){
              return new ModuleAction(a);
           });
       })
}

答案 2 :(得分:0)

我会使用flatMap和forkJoin:

this.http.get(url)
    .flatMap(t=> {
         let urls = getUrls(t.json())
             .map(url=> this.http.get(url).map(t=>getActions(t.json()));
         return Observable.forkJoin(urls).map(x=> [].concat(...x));           
     } 
//.subscribe => [ModuleAction, ModuleAction, ModuleAction,...]

其中

getUrls(body:[]): string[] {
    return body.map(jsModule => {
        let m = new Module(jsModule);
        let url = HttpHandlerService.URL_INFO + '/' + m.htmlPath;
        return url;
    };
}

getActions(body:any): ModuleAction[] {
     return body.actions.map((a:IModuleAction) => new ModuleAction(a));
}