RxJS forkJoin handle error on route resolve
Asked Answered
U

2

5

I can't seem to figure out a way to catch/handle errors inside forkJoin while trying to do route resolve.

I've created a route resolver for Account page and It should return 2 requests before routing. Now here is the part I cant work out: If user doesn't have a subscription then 404 is returned from server. I would like to handle this and if it happens, user should be routed to a different page, from where he could make subscription.

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Rx';
import { AccountService } from './account.service';

@Injectable()
export class AccountResolver implements Resolve<any> {
    constructor(private accountService: AccountService) { }

    resolve(route: ActivatedRouteSnapshot): Observable<any> {
        return Observable.forkJoin([
            this.accountService.getUser(),
            this.accountService.getUserSubscriptions()
        ]);
    };
}

How do I catch and handle error 404 sent by server when getUserSubscriptions() is requested?

Unrivalled answered 13/3, 2017 at 9:43 Comment(0)
S
4

You can catch the error, and redirect to desired route as follows:

@Injectable()
export class AccountResolver implements Resolve<any> {
    constructor(private accountService: AccountService,
                private router: Router) { }

    resolve(route: ActivatedRouteSnapshot): Observable<any> {
        return Observable.forkJoin([
            this.accountService.getUser(),
            this.accountService.getUserSubscriptions()
                .catch(error => {
                    if(error.status === 404) {
                        this.router.navigate(['subscription-create']);
                    }

                    return Observable.throw(error);
                })
        ]);
    };
}

This way, the error is handled, but also returned to the resolve method in its original shape, so the resolver can reject access to the previously targeted route (any failed request within the forkJoin will lead to failing the forkJoin itself by default).

Shroff answered 13/3, 2017 at 9:49 Comment(2)
Allready tried it and it didn't help. I got this error: Argument of type '(error: any) => void' is not assignable to parameter of type '(err: any, caught: Observable<UserSubscription>) => ObservableInput<UserSubscription, {}>'. Type 'void' is not assignable to type 'ObservableInput<UserSubscription, {}>'.Unrivalled
My problem here was that I didn't know that inside catch I would have to do return. Thanks a lot!Unrivalled
D
5

This depends where the error happens which is not entire obvious from your example.

If the getUserSubscriptions() call emits an error then you need to catch it before passing this to forkJoin because otherview the forkJoin will reemit the error. Also note that forkJoin requires each source Observable to emit at least one value. This is why I can't use Observable.empty() and I need to use for example Observable.of(null).

return Observable.forkJoin([
    this.accountService.getUser(),
    this.accountService.getUserSubscriptions()
        .catch(err => {
            // Logic based on the err parameter
            return Observable.of(null);
        })
]);

This will just replace the error with null so you can handle it in the subscriber to the forkJoin.

Delmerdelmor answered 13/3, 2017 at 9:53 Comment(1)
Just wondering, why would he catch the error and return the Observable instead? The point isn't to successfully resolve route dependencies, but inspect the 404 error and redirect accordingly..Shroff
S
4

You can catch the error, and redirect to desired route as follows:

@Injectable()
export class AccountResolver implements Resolve<any> {
    constructor(private accountService: AccountService,
                private router: Router) { }

    resolve(route: ActivatedRouteSnapshot): Observable<any> {
        return Observable.forkJoin([
            this.accountService.getUser(),
            this.accountService.getUserSubscriptions()
                .catch(error => {
                    if(error.status === 404) {
                        this.router.navigate(['subscription-create']);
                    }

                    return Observable.throw(error);
                })
        ]);
    };
}

This way, the error is handled, but also returned to the resolve method in its original shape, so the resolver can reject access to the previously targeted route (any failed request within the forkJoin will lead to failing the forkJoin itself by default).

Shroff answered 13/3, 2017 at 9:49 Comment(2)
Allready tried it and it didn't help. I got this error: Argument of type '(error: any) => void' is not assignable to parameter of type '(err: any, caught: Observable<UserSubscription>) => ObservableInput<UserSubscription, {}>'. Type 'void' is not assignable to type 'ObservableInput<UserSubscription, {}>'.Unrivalled
My problem here was that I didn't know that inside catch I would have to do return. Thanks a lot!Unrivalled

© 2022 - 2024 — McMap. All rights reserved.