1

Good morning to everyone. I'm a newbie of Angular2 and in a project I'm working on I have to make some HTTP requests in order to retrieve data from a REST backend.

From the backend I have to get a list of Modules. Each Module has one or more Actions. The typescript classes representing the two entities are the following

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

The backed exposes a call to retrieve a list of modules without the relative actions and a second endpoint to retrieve a list of actions for a given module. In order to initialize my angular interface I have to make a first HTTP request to get the modules list. Once obtained the modules I have to make a second request for each one of them to retrieve the actions and update the Module instance.

Yesterday I tried to use RxJS to get the data. The only working solution I found has been the following

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);
}

I'm quite sure that I'm wrongly using the RxJS operator and there must be a different solution to obtain the same result in a more "RxJS compliant" way.

How can I use RxJS operators in order to obtain the same result? Thanks a lot for your help.

hara
  • 3,274
  • 4
  • 37
  • 55
  • what error r u getting?? – Vinay Pandya Mar 16 '17 at 09:03
  • @VinayPandya I'm nnot getting any error. I'm just wondering if there is a better way using RxJS operator to do the same – hara Mar 16 '17 at 09:24
  • Have a look at http://stackoverflow.com/documentation/rxjs/8247/common-recipes/27973/sending-multiple-parallel-http-requests#t=20170316111633998635 or http://stackoverflow.com/documentation/rxjs/8247/common-recipes/28035/sending-multiple-sequential-http-requests#t=201703161116455216797 – martin Mar 16 '17 at 11:17
  • @hara as seidme suggested use .flatMap() which is very help full. i also recommend that. – Vinay Pandya Mar 17 '17 at 05:13

3 Answers3

3

Chaining interdependent HTTP requests can be achieved using the Observable.flatMap operator. See this post: Angular 2 - Chaining http requests

And, since you need to retrieve module actions for each module, use Observable.forkJoin operator. See this post: Send multiple HTTP GET requests in parallel

The final structure would look something like:

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: [..]}]
    });
Community
  • 1
  • 1
seidme
  • 12,543
  • 5
  • 36
  • 40
0

try to make some separations and use 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);
           });
       })
}
El houcine bougarfaoui
  • 35,805
  • 9
  • 37
  • 37
0

I would use flatMap and 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,...]

Where

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));
}
Michael Kang
  • 52,003
  • 16
  • 103
  • 135