From 309944931f2dd9d6d9ea1626acf83fe697cdb113 Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Mon, 14 Sep 2015 11:22:54 -0700 Subject: [PATCH] fix(router): recognize child components with empty segments Previosly, recognition ended when a parent captured all the parsed URL segments. This caused routes that delegated from a parent to a child with an empty segment to never be recognized. Closes #4178 --- .../angular2/src/router/path_recognizer.ts | 42 +++++++++---------- modules/angular2/src/router/route_registry.ts | 10 ++--- .../router/integration/navigation_spec.ts | 15 ++++++- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/modules/angular2/src/router/path_recognizer.ts b/modules/angular2/src/router/path_recognizer.ts index f22a666cce..860b28bbfa 100644 --- a/modules/angular2/src/router/path_recognizer.ts +++ b/modules/angular2/src/router/path_recognizer.ts @@ -20,7 +20,7 @@ import {RouteHandler} from './route_handler'; import {Url, RootUrl, serializeParams} from './url_parser'; import {ComponentInstruction} from './instruction'; -export class TouchMap { +class TouchMap { map: StringMap = {}; keys: StringMap = {}; @@ -54,7 +54,7 @@ function normalizeString(obj: any): string { } } -export interface Segment { +interface Segment { name: string; generate(params: TouchMap): string; match(path: string): boolean; @@ -75,7 +75,7 @@ class StaticSegment implements Segment { class DynamicSegment implements Segment { constructor(public name: string) {} - match(path: string): boolean { return true; } + match(path: string): boolean { return path.length > 0; } generate(params: TouchMap): string { if (!StringMapWrapper.contains(params.map, this.name)) { throw new BaseException( @@ -224,26 +224,26 @@ export class PathRecognizer { break; } - if (isBlank(currentSegment)) { + if (isPresent(currentSegment)) { + captured.push(currentSegment.path); + + // the star segment consumes all of the remaining URL, including matrix params + if (segment instanceof StarSegment) { + positionalParams[segment.name] = currentSegment.toString(); + nextSegment = null; + break; + } + + if (segment instanceof DynamicSegment) { + positionalParams[segment.name] = currentSegment.path; + } else if (!segment.match(currentSegment.path)) { + return null; + } + + nextSegment = currentSegment.child; + } else if (!segment.match('')) { return null; } - - captured.push(currentSegment.path); - - // the star segment consumes all of the remaining URL, including matrix params - if (segment instanceof StarSegment) { - positionalParams[segment.name] = currentSegment.toString(); - nextSegment = null; - break; - } - - if (segment instanceof DynamicSegment) { - positionalParams[segment.name] = currentSegment.path; - } else if (!segment.match(currentSegment.path)) { - return null; - } - - nextSegment = currentSegment.child; } if (this.terminal && isPresent(nextSegment)) { diff --git a/modules/angular2/src/router/route_registry.ts b/modules/angular2/src/router/route_registry.ts index 22eaeb5d21..5b8793bd53 100644 --- a/modules/angular2/src/router/route_registry.ts +++ b/modules/angular2/src/router/route_registry.ts @@ -120,7 +120,7 @@ export class RouteRegistry { private _recognizePrimaryRoute(parsedUrl: Url, parentComponent): Promise { var componentRecognizer = this._rules.get(parentComponent); if (isBlank(componentRecognizer)) { - return PromiseWrapper.resolve(null); + return _resolveToNull; } // Matches some beginning part of the given URL @@ -137,12 +137,8 @@ export class RouteRegistry { return instruction.resolveComponentType().then((componentType) => { this.configFromComponent(componentType); - if (isBlank(partialMatch.remaining)) { - if (instruction.terminal) { - return new PrimaryInstruction(instruction, null, partialMatch.remainingAux); - } else { - return null; - } + if (instruction.terminal) { + return new PrimaryInstruction(instruction, null, partialMatch.remainingAux); } return this._recognizePrimaryRoute(partialMatch.remaining, componentType) diff --git a/modules/angular2/test/router/integration/navigation_spec.ts b/modules/angular2/test/router/integration/navigation_spec.ts index 4e376f19de..cf009c410a 100644 --- a/modules/angular2/test/router/integration/navigation_spec.ts +++ b/modules/angular2/test/router/integration/navigation_spec.ts @@ -115,6 +115,18 @@ export function main() { }); })); + it('should navigate to child routes that capture an empty path', + inject([AsyncTestCompleter], (async) => { + compile('outer { }') + .then((_) => rtr.config([new Route({path: '/a/...', component: ParentCmp})])) + .then((_) => rtr.navigateByUrl('/a')) + .then((_) => { + rootTC.detectChanges(); + expect(rootTC.debugElement.nativeElement).toHaveText('outer { inner { hello } }'); + async.done(); + }); + })); + it('should navigate to child routes of async routes', inject([AsyncTestCompleter], (async) => { compile('outer { }') @@ -309,7 +321,8 @@ function parentLoader() { @Component({selector: 'parent-cmp'}) @View({template: "inner { }", directives: [RouterOutlet]}) -@RouteConfig([new Route({path: '/b', component: HelloCmp})]) +@RouteConfig( + [new Route({path: '/b', component: HelloCmp}), new Route({path: '/', component: HelloCmp})]) class ParentCmp { constructor() {} }