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.

4

3 Answers 3

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: [..]}]
    });
Sign up to request clarification or add additional context in comments.

Comments

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

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.