New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(router): rethrow exceptions #2528
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ import {routerInjectables, Router} from 'angular2/router'; | |
import {RouterOutlet} from 'angular2/src/router/router_outlet'; | ||
import {SpyLocation} from 'angular2/src/mock/location_mock'; | ||
import {Location} from 'angular2/src/router/location'; | ||
import {PromiseWrapper} from 'angular2/src/facade/async'; | ||
import {BaseException} from 'angular2/src/facade/lang'; | ||
|
||
export function main() { | ||
describe('router injectables', () => { | ||
|
@@ -47,6 +49,19 @@ export function main() { | |
}); | ||
})); | ||
|
||
it('should rethrow exceptions from component constructors', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @btford does this test fail without the change in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nope (tested this time) |
||
inject([AsyncTestCompleter], (async) => { | ||
bootstrap(BrokenAppCmp, testBindings) | ||
.then((applicationRef) => { | ||
var router = applicationRef.hostComponent.router; | ||
PromiseWrapper.catchError(router.navigate('/cause-error'), (error) => { | ||
expect(el).toHaveText('outer { oh no }'); | ||
expect(error.message).toBe('oops!'); | ||
async.done(); | ||
}); | ||
}); | ||
})); | ||
|
||
// TODO: add a test in which the child component has bindings | ||
}); | ||
} | ||
|
@@ -57,11 +72,24 @@ export function main() { | |
class HelloCmp { | ||
} | ||
|
||
|
||
@Component({selector: 'app-cmp'}) | ||
@View({template: "outer { <router-outlet></router-outlet> }", directives: [RouterOutlet]}) | ||
@RouteConfig([{path: '/', component: HelloCmp}]) | ||
class AppCmp { | ||
router: Router; | ||
constructor(router: Router) { this.router = router; } | ||
} | ||
|
||
@Component({selector: 'oops-cmp'}) | ||
@View({template: "oh no"}) | ||
class BrokenCmp { | ||
constructor() { throw new BaseException('oops!'); } | ||
} | ||
|
||
@Component({selector: 'app-cmp'}) | ||
@View({template: "outer { <router-outlet></router-outlet> }", directives: [RouterOutlet]}) | ||
@RouteConfig([{path: '/cause-error', component: BrokenCmp}]) | ||
class BrokenAppCmp { | ||
router: Router; | ||
constructor(router: Router) { this.router = router; } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
noop ? (the return value is not used)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same as
.catch
– so the error will be propagated to anyone consuming this promise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are 2 anti-patterns here:
Current Code
It should be
Note than the concise way is a slightly different behavior: if an exception is thrown is the
thenFn
, thecatchFn
is not called. If you want tocatchFn
to also handle errors in thethenFn
, you should go for the first code.catchFn
From an
Promise
reject function, you can either:p = rejectedP.catch(e => return value)
this is "recovery", p will be aPromise
that resolves tovalue
(it is not a rejectedPromise
.p = rejected.catch(e => throw new Error())
equivalent top = rejected.catch(e => Promise.reject(new Error()))
, p will be a rejectedPromise
- the first form is probably more usual but sometimes creates issue with TS which expects a return value for the callbackThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 See #2565