angular: Exception/Error In Resolvers (Router) can not be catched in the subscription to "activatedRoute.data"
I’m submitting a … (check one with “x”)
[X ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior
I’m using the Router with Resolver interfaces to get some data form a service (backend) before creating a component which will render the data (subscribing to it). If an exception occurs in the Resolver the application breaks. (Error: Uncaught (in promise))
Expected behavior
If there is an exception in the Resolver i expect the subscribe error function to be called but the ErrorObservable appears in the “activatedRoute.data.value” instead.
// some component
ngOnInit() {
this.activatedRoute.data
.subscribe(
(data) => {
this.data = data.data] || {};
console.log('data was set to ' + this.data)
},
(error) => {
/// we never get here, no matter there was a Promise.reject(), throw Error(), etc.
console.log('ups! Error getting data')
alert(error);
});
}
// some resolver
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BehaviorSubject<any>|Observable<any>|Promise<any>|any {
return Observable.throw('This always fails');
}
Minimal reproduction of the problem with instructions
check out the plunker http://plnkr.co/edit/MaiM6T1COr2ppB2AMANx
What is the motivation / use case for changing the behavior?
Catch and treat exceptions within the target component Please tell us about your environment:
- Angular version: 2.0.X 2.3.1
Yes
- Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
All
- Language: [all | TypeScript X.X | ES6/7 | ES5] TypeScript 2.1.1
- Node (for AoT issues):
node --version
=
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 28
- Comments: 29 (5 by maintainers)
I totally agree with @OscarGalindo . The problem here is that you can not take over the router error handling (or the resolver’s). If the the resolver fails, the router simply does not do the navigation. So from my modest point of view we have 4 options here:
{ path: 'products/:id', component: ProductComponent, resolve: { data: ProductService errorComponent: ProductErrorComponent }, }
Honestly, I would go for solution 2 which was the initial idea and the most intuitive one.
is more flexible solution as to me, than 1 time observable
I’ll be completely honest, I thought about this for a while, and did some preliminary research into what would need to be done to fix it, and I’m beginning to disagree that this is a bug, and that @DzmitryShylovich’s workaround is actually the proper solution.
The purpose of a resolver is to resolve critical data that must exist before the route even loads. If you’re dealing with data that does not have to be resolved pre-activation, then you shouldn’t use a resolver anyway, and just load the data in ngOnInit(). Therefore, it wouldn’t make sense to continue with a successful navigation if a resolver fails with an error, because that would mean that critical data required for the route failed to load. Attempting to catch the error from the resolver is conflating a failure in navigation with a failure in application-specific business logic. If a component expects to successfully load and receive information regarding a failed resolver, then that is still a navigation success, albeit an application failure. Therefore, it is the resolver’s responsibility to catch any application errors, and return them as a navigation success in a data structure that properly communicates the state of the resolver to the component.
I think the actual bug here is that ActivatedRoute.data is an Observable in the first place, if it is already resolved when the component is loaded. But this is really just misleading rather than a functional bug.
I used the Resolve example from thoughtram and modified it so that it would break in the route.
https://plnkr.co/edit/wLosw5 That is,
ContactResolve
will callContactsService.failGetContact
which performsObservable.throw('...');
When this happens, the exception is logged on the console and the route stays onContactsListComponent
.I want to handle this error at the component level. But the
ContactsDetailComponent
is never loaded. Should I be handling the error in theContactsListComponent
orContactsDetailComponent
? If on CLC, how?@Parent5446
I will agree that your logic is sound regarding the reason for a resolver guard in the first place (must resolve data before a route can be resolved) but I disagree that this isn’t a fundamental bug for a number of reasons:
While I realize there are workarounds for all these things it is unnecessarily cumbersome and arguably goes against the ES standards because we are expected to believe that a Promise in = Promise out regardless of level.
Somewhat off topic, but, if anything, in my opinion, the Router in Angular needs a complete re-write. It’s easily the worst part.
I think that we can pass an error from a resolver to a component right now. See my example on stackblitz:
But it would be nice if the resolver could accept some thing like this:
or just:
And if
SomeResolverService
fail, then user can go to/route-path
, but it will seeHandleErrorComponent
Same problem here using 2.4.2.
I did a “catch” in the resolve which returns an empty observable and check inside component if empty or not.