Skip to content

Commit 3099449

Browse files
committed
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 angular#4178
1 parent 63e7859 commit 3099449

File tree

3 files changed

+35
-26
lines changed

3 files changed

+35
-26
lines changed

modules/angular2/src/router/path_recognizer.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {RouteHandler} from './route_handler';
2020
import {Url, RootUrl, serializeParams} from './url_parser';
2121
import {ComponentInstruction} from './instruction';
2222

23-
export class TouchMap {
23+
class TouchMap {
2424
map: StringMap<string, string> = {};
2525
keys: StringMap<string, boolean> = {};
2626

@@ -54,7 +54,7 @@ function normalizeString(obj: any): string {
5454
}
5555
}
5656

57-
export interface Segment {
57+
interface Segment {
5858
name: string;
5959
generate(params: TouchMap): string;
6060
match(path: string): boolean;
@@ -75,7 +75,7 @@ class StaticSegment implements Segment {
7575

7676
class DynamicSegment implements Segment {
7777
constructor(public name: string) {}
78-
match(path: string): boolean { return true; }
78+
match(path: string): boolean { return path.length > 0; }
7979
generate(params: TouchMap): string {
8080
if (!StringMapWrapper.contains(params.map, this.name)) {
8181
throw new BaseException(
@@ -224,26 +224,26 @@ export class PathRecognizer {
224224
break;
225225
}
226226

227-
if (isBlank(currentSegment)) {
228-
return null;
229-
}
227+
if (isPresent(currentSegment)) {
228+
captured.push(currentSegment.path);
230229

231-
captured.push(currentSegment.path);
230+
// the star segment consumes all of the remaining URL, including matrix params
231+
if (segment instanceof StarSegment) {
232+
positionalParams[segment.name] = currentSegment.toString();
233+
nextSegment = null;
234+
break;
235+
}
232236

233-
// the star segment consumes all of the remaining URL, including matrix params
234-
if (segment instanceof StarSegment) {
235-
positionalParams[segment.name] = currentSegment.toString();
236-
nextSegment = null;
237-
break;
238-
}
237+
if (segment instanceof DynamicSegment) {
238+
positionalParams[segment.name] = currentSegment.path;
239+
} else if (!segment.match(currentSegment.path)) {
240+
return null;
241+
}
239242

240-
if (segment instanceof DynamicSegment) {
241-
positionalParams[segment.name] = currentSegment.path;
242-
} else if (!segment.match(currentSegment.path)) {
243+
nextSegment = currentSegment.child;
244+
} else if (!segment.match('')) {
243245
return null;
244246
}
245-
246-
nextSegment = currentSegment.child;
247247
}
248248

249249
if (this.terminal && isPresent(nextSegment)) {

modules/angular2/src/router/route_registry.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export class RouteRegistry {
120120
private _recognizePrimaryRoute(parsedUrl: Url, parentComponent): Promise<PrimaryInstruction> {
121121
var componentRecognizer = this._rules.get(parentComponent);
122122
if (isBlank(componentRecognizer)) {
123-
return PromiseWrapper.resolve(null);
123+
return _resolveToNull;
124124
}
125125

126126
// Matches some beginning part of the given URL
@@ -137,12 +137,8 @@ export class RouteRegistry {
137137
return instruction.resolveComponentType().then((componentType) => {
138138
this.configFromComponent(componentType);
139139

140-
if (isBlank(partialMatch.remaining)) {
141-
if (instruction.terminal) {
142-
return new PrimaryInstruction(instruction, null, partialMatch.remainingAux);
143-
} else {
144-
return null;
145-
}
140+
if (instruction.terminal) {
141+
return new PrimaryInstruction(instruction, null, partialMatch.remainingAux);
146142
}
147143

148144
return this._recognizePrimaryRoute(partialMatch.remaining, componentType)

modules/angular2/test/router/integration/navigation_spec.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ export function main() {
115115
});
116116
}));
117117

118+
it('should navigate to child routes that capture an empty path',
119+
inject([AsyncTestCompleter], (async) => {
120+
compile('outer { <router-outlet></router-outlet> }')
121+
.then((_) => rtr.config([new Route({path: '/a/...', component: ParentCmp})]))
122+
.then((_) => rtr.navigateByUrl('/a'))
123+
.then((_) => {
124+
rootTC.detectChanges();
125+
expect(rootTC.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
126+
async.done();
127+
});
128+
}));
129+
118130

119131
it('should navigate to child routes of async routes', inject([AsyncTestCompleter], (async) => {
120132
compile('outer { <router-outlet></router-outlet> }')
@@ -309,7 +321,8 @@ function parentLoader() {
309321

310322
@Component({selector: 'parent-cmp'})
311323
@View({template: "inner { <router-outlet></router-outlet> }", directives: [RouterOutlet]})
312-
@RouteConfig([new Route({path: '/b', component: HelloCmp})])
324+
@RouteConfig(
325+
[new Route({path: '/b', component: HelloCmp}), new Route({path: '/', component: HelloCmp})])
313326
class ParentCmp {
314327
constructor() {}
315328
}

0 commit comments

Comments
 (0)