F.Almeida
F.Almeida

Reputation: 413

Angular 2 wait for nested observables to finish

I need to wait for two of my nested Observables to be completed before navigating to another page. I don't know what is the best way to do this since they are nested, therefore I'm having sync problems in my Angular app. The Observables are being set at my authentication service. authentication.service.ts:

login(username: string, password: string) {
        let reqUrl = AppSettings.__USER_TOKEN_URL;
        let reqHeaders = this.authConfig.token.headers;
        let reqBody = encodeURI(
            this.authConfig.token.body
                .replace(/{{ username }}/g, username)
                .replace(/{{ password }}/g, password));

        //
        // Get token, then get user identity, if login successfull.
        //

        return this.http.post(reqUrl, reqBody, reqHeaders)
            .map((response) => this.getIdentity(response))
            .catch(this.handleErr);
    }

private getIdentity(response: Response) {

        //
        // Get user identity based on token.
        //

        let body = response.json();
        let token = body.access_token;

        if (null != token && undefined != token) {
            this.authConfig
                .identity
                .headers
                .headers.set('authorization', 'Bearer ' + token);

            let reqUrl = AppSettings.__USER_IDENTITY_URL
            let reqHeaders = this.authConfig.identity.headers;
            let reqbody = this.authConfig.identity.body;

            return this.http.post(reqUrl, reqbody, reqHeaders)
                .map((response) => this.setUser(response))
                .catch(this.handleErr)
                .subscribe();
        }
    }

Then at my Login component, I'm trying to call the service login() method, and when it is finished, I want to navigate to another instance. login.component.ts:

login() {
        this.loading = true;
        this.authenticationService.login(this.model.username, this.model.password).subscribe(
            data => { },
            error => { console.log('Error authenticating: ' + error); },
            () => {  this.router.navigate([this.returnUrl]) });
    }

But it's not working. The Observables are still running when router.navigate is triggered. Any ideas for an Angular rookie? Thanks in advance.

Upvotes: 3

Views: 1161

Answers (1)

Suraj Rao
Suraj Rao

Reputation: 29635

The problem is you are simply calling subscribe inside getIdentity() which doesn't make the two observables sequential.

Instead, you need to return the observable and not the subscription object there and use switchMap.

getIdentity:

private getIdentity(response: Response) {

        //
        // Get user identity based on token.
        //

        let body = response.json();
        let token = body.access_token;

        if (null != token && undefined != token) {
            this.authConfig
                .identity
                .headers
                .headers.set('authorization', 'Bearer ' + token);

            let reqUrl = AppSettings.__USER_IDENTITY_URL
            let reqHeaders = this.authConfig.identity.headers;
            let reqbody = this.authConfig.identity.body;

            return this.http.post(reqUrl, reqbody, reqHeaders)
                .map((response) => this.setUser(response))//return observable.
        }
}

In the login call:

return this.http.post(reqUrl, reqBody, reqHeaders)
        .switchMap((response) => this.getIdentity(response))
        .catch(this.handleErr);

The switchMap will switch to the second observable and return it on completion of the first.

Upvotes: 0

Related Questions