Stanislasdrg Stanislasdrg - 1 month ago 30
TypeScript Question

Angular 2 : Chain Observables in Resolver

I am new to Angular2 and observables. I am trying to return the result of a chain of observables to my resolver and couldn't find an answer on SO. Everything works fine when I return only one observable, but fails when those are chained. I've simplified the code to calling consecutively twice the same test function :

@Injectable()
export class SwResolve implements Resolve<any> {
constructor(private swService: SwService) {}
resolve (route: ActivatedRouteSnapshot): Observable<MyObject> | Promise<any> | any {
this.swService.getTestData().subscribe(data => {
return this.swService.getTestData();
});
}
}


SwComponent :

export class SwComponent implements OnInit {constructor(
private route: ActivatedRoute,
private router: Router
) { }

ngOnInit(): void {
this.route.data.forEach((data: any) => {
// Handle data received
});
}
};


SwService :

getTestData() {
return Observable.create(observer => {
let testObject : SwDataObject = {
'labels':['value0','value1'],
'desktop':[123,456],
'mobile':[789,1011]
};
observer.next(testObject);
observer.complete();
});
}


What am I doing wrong here ?

Thanks !

Answer

The resolve function expects an observable to be returned. What you are doing isn't correct since you are only returning an async result. You are calling the first observable and then subscribe to that observable. So time passes and only if the first observable is resolved, you are returning an observable.

What you should be using for stuff like this (which has similar behaviour to Promise.then (kind of)) is a flatMap/mergeMap (aliases in RxJS5).

Refactor your code to this:

@Injectable()
export class SwResolve implements Resolve<any> {
    constructor(private swService: SwService) {}    
        resolve (route: ActivatedRouteSnapshot): Observable<MyObject> | Promise<any> | any {
             return this.swService.getTestData().mergeMap(data => 
                 return this.swService.getTestData();
             );
        }
}

What this will do is return an observable immediately.

What mergeMap does is get an event as input (in this case the result from your first getTestData()-call) and expect you to return another observable. Under the hood it will subscribe to this observable and flatten the result.

There are a ton of articles on mergeMap so if this is unclear, I suggest you read a few. Here's a good start: https://github.com/btroncone/learn-rxjs/blob/master/operators/transformation/mergemap.md