From 4e6c4a8b2e75935917e7fbfb915e492ab2dad7b2 Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Tue, 15 Dec 2015 15:58:04 -0800 Subject: [PATCH] fix(router): preserve specificity for redirects Previously when comparing which of multiple possible routes to choose in an ambiguous case, we looked at the specificity of the target of redirect matches rather than the original match. This meant that if a redirect used a whilecard, but redirected to a target that was a static path, we'd cound the static path's specificity instead of the wildcard. This change stores the specificity of the redirect on the RedirectInstruction. --- modules/angular2/src/router/instruction.ts | 4 +++- modules/angular2/src/router/route_registry.ts | 2 +- .../integration/impl/fixture_components.ts | 6 ++++++ .../router/integration/redirect_route_spec.ts | 21 ++++++++++++++++++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/modules/angular2/src/router/instruction.ts b/modules/angular2/src/router/instruction.ts index 815745bd920d..817e91a56da0 100644 --- a/modules/angular2/src/router/instruction.ts +++ b/modules/angular2/src/router/instruction.ts @@ -281,9 +281,11 @@ export class UnresolvedInstruction extends Instruction { export class RedirectInstruction extends ResolvedInstruction { constructor(component: ComponentInstruction, child: Instruction, - auxInstruction: {[key: string]: Instruction}) { + auxInstruction: {[key: string]: Instruction}, private _specificity: string) { super(component, child, auxInstruction); } + + get specificity(): string { return this._specificity; } } diff --git a/modules/angular2/src/router/route_registry.ts b/modules/angular2/src/router/route_registry.ts index dbf757f4a779..5750396c29ed 100644 --- a/modules/angular2/src/router/route_registry.ts +++ b/modules/angular2/src/router/route_registry.ts @@ -205,7 +205,7 @@ export class RouteRegistry { var instruction = this.generate(candidate.redirectTo, ancestorInstructions.concat([null])); return new RedirectInstruction(instruction.component, instruction.child, - instruction.auxInstruction); + instruction.auxInstruction, candidate.specificity); } })); diff --git a/modules/angular2/test/router/integration/impl/fixture_components.ts b/modules/angular2/test/router/integration/impl/fixture_components.ts index 075213bdf67b..5777bf163bda 100644 --- a/modules/angular2/test/router/integration/impl/fixture_components.ts +++ b/modules/angular2/test/router/integration/impl/fixture_components.ts @@ -10,6 +10,12 @@ import { } from 'angular2/router'; import {PromiseWrapper} from 'angular2/src/facade/async'; +@Component({selector: 'goodbye-cmp', template: `{{farewell}}`}) +export class GoodbyeCmp { + farewell: string; + constructor() { this.farewell = 'goodbye'; } +} + @Component({selector: 'hello-cmp', template: `{{greeting}}`}) export class HelloCmp { greeting: string; diff --git a/modules/angular2/test/router/integration/redirect_route_spec.ts b/modules/angular2/test/router/integration/redirect_route_spec.ts index 4c2f195b3dc4..963fc78e3067 100644 --- a/modules/angular2/test/router/integration/redirect_route_spec.ts +++ b/modules/angular2/test/router/integration/redirect_route_spec.ts @@ -25,7 +25,7 @@ import { } from 'angular2/src/router/route_config_decorator'; import {TEST_ROUTER_PROVIDERS, RootCmp, compile} from './util'; -import {HelloCmp, RedirectToParentCmp} from './impl/fixture_components'; +import {HelloCmp, GoodbyeCmp, RedirectToParentCmp} from './impl/fixture_components'; var cmpInstanceCount; var childCmpInstanceCount; @@ -117,5 +117,24 @@ export function main() { async.done(); }); })); + + + it('should not redirect when redirect is less specific than other matching routes', + inject([AsyncTestCompleter, Location], (async, location) => { + compile(tcb) + .then((rtc) => {rootTC = rtc}) + .then((_) => rtr.config([ + new Route({path: '/foo', component: HelloCmp, name: 'Hello'}), + new Route({path: '/:param', component: GoodbyeCmp, name: 'Goodbye'}), + new Redirect({path: '/*rest', redirectTo: ['/Hello']}) + ])) + .then((_) => rtr.navigateByUrl('/bye')) + .then((_) => { + rootTC.detectChanges(); + expect(rootTC.debugElement.nativeElement).toHaveText('goodbye'); + expect(location.urlChanges).toEqual(['/bye']); + async.done(); + }); + })); }); }