fix(router): handle URL that does not match a route

Closes #7349
Closes #7203
This commit is contained in:
Peter Bacon Darwin 2016-03-02 15:04:08 +00:00 committed by Alex Eagle
parent aa43d2f87b
commit 8e3e45097a
2 changed files with 47 additions and 26 deletions

View File

@ -260,6 +260,7 @@ export class Router {
}
private _emitNavigationFinish(url): void { ObservableWrapper.callEmit(this._subject, url); }
_emitNavigationFail(url): void { ObservableWrapper.callError(this._subject, url); }
private _afterPromiseFinishNavigating(promise: Promise<any>): Promise<any> {
return PromiseWrapper.catchError(promise.then((_) => this._finishNavigating()), (err) => {
@ -367,8 +368,8 @@ export class Router {
/**
* Subscribe to URL updates from the router
*/
subscribe(onNext: (value: any) => void): Object {
return ObservableWrapper.subscribe(this._subject, onNext);
subscribe(onNext: (value: any) => void, onError?: (value: any) => void): Object {
return ObservableWrapper.subscribe(this._subject, onNext, onError);
}
@ -451,31 +452,35 @@ export class RootRouter extends Router {
// we call recognize ourselves
this.recognize(change['url'])
.then((instruction) => {
this.navigateByInstruction(instruction, isPresent(change['pop']))
.then((_) => {
// this is a popstate event; no need to change the URL
if (isPresent(change['pop']) && change['type'] != 'hashchange') {
return;
}
var emitPath = instruction.toUrlPath();
var emitQuery = instruction.toUrlQuery();
if (emitPath.length > 0 && emitPath[0] != '/') {
emitPath = '/' + emitPath;
}
// Because we've opted to use All hashchange events occur outside Angular.
// However, apps that are migrating might have hash links that operate outside
// angular to which routing must respond.
// To support these cases where we respond to hashchanges and redirect as a
// result, we need to replace the top item on the stack.
if (change['type'] == 'hashchange') {
if (instruction.toRootUrl() != this._location.path()) {
this._location.replaceState(emitPath, emitQuery);
if (isPresent(instruction)) {
this.navigateByInstruction(instruction, isPresent(change['pop']))
.then((_) => {
// this is a popstate event; no need to change the URL
if (isPresent(change['pop']) && change['type'] != 'hashchange') {
return;
}
} else {
this._location.go(emitPath, emitQuery);
}
});
var emitPath = instruction.toUrlPath();
var emitQuery = instruction.toUrlQuery();
if (emitPath.length > 0 && emitPath[0] != '/') {
emitPath = '/' + emitPath;
}
// Because we've opted to use All hashchange events occur outside Angular.
// However, apps that are migrating might have hash links that operate outside
// angular to which routing must respond.
// To support these cases where we respond to hashchanges and redirect as a
// result, we need to replace the top item on the stack.
if (change['type'] == 'hashchange') {
if (instruction.toRootUrl() != this._location.path()) {
this._location.replaceState(emitPath, emitQuery);
}
} else {
this._location.go(emitPath, emitQuery);
}
});
} else {
this._emitNavigationFail(change['url']);
}
});
});

View File

@ -145,6 +145,22 @@ export function main() {
});
}));
it('should trigger the onError callback of a router change subscription if the URL does not match a route',
inject([AsyncTestCompleter], (async) => {
var outlet = makeDummyOutlet();
router.registerPrimaryOutlet(outlet)
.then((_) => router.config([new Route({path: '/a', component: DummyComponent})]))
.then((_) => {
router.subscribe((_) => {}, (url) => {
expect(url).toEqual('b');
async.done();
});
(<SpyLocation>location).simulateHashChange('b');
});
}));
it('should navigate after being configured', inject([AsyncTestCompleter], (async) => {
var outlet = makeDummyOutlet();