X Tutup
Skip to content

Commit 8e3e450

Browse files
petebacondarwinalexeagle
authored andcommitted
fix(router): handle URL that does not match a route
Closes #7349 Closes #7203
1 parent aa43d2f commit 8e3e450

File tree

2 files changed

+47
-26
lines changed

2 files changed

+47
-26
lines changed

modules/angular2/src/router/router.ts

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ export class Router {
260260
}
261261

262262
private _emitNavigationFinish(url): void { ObservableWrapper.callEmit(this._subject, url); }
263+
_emitNavigationFail(url): void { ObservableWrapper.callError(this._subject, url); }
263264

264265
private _afterPromiseFinishNavigating(promise: Promise<any>): Promise<any> {
265266
return PromiseWrapper.catchError(promise.then((_) => this._finishNavigating()), (err) => {
@@ -367,8 +368,8 @@ export class Router {
367368
/**
368369
* Subscribe to URL updates from the router
369370
*/
370-
subscribe(onNext: (value: any) => void): Object {
371-
return ObservableWrapper.subscribe(this._subject, onNext);
371+
subscribe(onNext: (value: any) => void, onError?: (value: any) => void): Object {
372+
return ObservableWrapper.subscribe(this._subject, onNext, onError);
372373
}
373374

374375

@@ -451,31 +452,35 @@ export class RootRouter extends Router {
451452
// we call recognize ourselves
452453
this.recognize(change['url'])
453454
.then((instruction) => {
454-
this.navigateByInstruction(instruction, isPresent(change['pop']))
455-
.then((_) => {
456-
// this is a popstate event; no need to change the URL
457-
if (isPresent(change['pop']) && change['type'] != 'hashchange') {
458-
return;
459-
}
460-
var emitPath = instruction.toUrlPath();
461-
var emitQuery = instruction.toUrlQuery();
462-
if (emitPath.length > 0 && emitPath[0] != '/') {
463-
emitPath = '/' + emitPath;
464-
}
465-
466-
// Because we've opted to use All hashchange events occur outside Angular.
467-
// However, apps that are migrating might have hash links that operate outside
468-
// angular to which routing must respond.
469-
// To support these cases where we respond to hashchanges and redirect as a
470-
// result, we need to replace the top item on the stack.
471-
if (change['type'] == 'hashchange') {
472-
if (instruction.toRootUrl() != this._location.path()) {
473-
this._location.replaceState(emitPath, emitQuery);
455+
if (isPresent(instruction)) {
456+
this.navigateByInstruction(instruction, isPresent(change['pop']))
457+
.then((_) => {
458+
// this is a popstate event; no need to change the URL
459+
if (isPresent(change['pop']) && change['type'] != 'hashchange') {
460+
return;
474461
}
475-
} else {
476-
this._location.go(emitPath, emitQuery);
477-
}
478-
});
462+
var emitPath = instruction.toUrlPath();
463+
var emitQuery = instruction.toUrlQuery();
464+
if (emitPath.length > 0 && emitPath[0] != '/') {
465+
emitPath = '/' + emitPath;
466+
}
467+
468+
// Because we've opted to use All hashchange events occur outside Angular.
469+
// However, apps that are migrating might have hash links that operate outside
470+
// angular to which routing must respond.
471+
// To support these cases where we respond to hashchanges and redirect as a
472+
// result, we need to replace the top item on the stack.
473+
if (change['type'] == 'hashchange') {
474+
if (instruction.toRootUrl() != this._location.path()) {
475+
this._location.replaceState(emitPath, emitQuery);
476+
}
477+
} else {
478+
this._location.go(emitPath, emitQuery);
479+
}
480+
});
481+
} else {
482+
this._emitNavigationFail(change['url']);
483+
}
479484
});
480485
});
481486

modules/angular2/test/router/router_spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,22 @@ export function main() {
145145
});
146146
}));
147147

148+
149+
it('should trigger the onError callback of a router change subscription if the URL does not match a route',
150+
inject([AsyncTestCompleter], (async) => {
151+
var outlet = makeDummyOutlet();
152+
153+
router.registerPrimaryOutlet(outlet)
154+
.then((_) => router.config([new Route({path: '/a', component: DummyComponent})]))
155+
.then((_) => {
156+
router.subscribe((_) => {}, (url) => {
157+
expect(url).toEqual('b');
158+
async.done();
159+
});
160+
(<SpyLocation>location).simulateHashChange('b');
161+
});
162+
}));
163+
148164
it('should navigate after being configured', inject([AsyncTestCompleter], (async) => {
149165
var outlet = makeDummyOutlet();
150166

0 commit comments

Comments
 (0)
X Tutup