fix(router): handle lastPathIndex of empty-path routes

This commit is contained in:
vsavkin 2016-07-18 14:53:13 -07:00
parent 83bc5c97ef
commit 7a4f6621ed
3 changed files with 105 additions and 17 deletions

View File

@ -116,7 +116,8 @@ function processPathsWithParamsAgainstRoute(
const snapshot = new ActivatedRouteSnapshot( const snapshot = new ActivatedRouteSnapshot(
paths, Object.freeze(merge(inherited.allParams, params)), paths, Object.freeze(merge(inherited.allParams, params)),
merge(inherited.allData, getData(route)), outlet, route.component, route, merge(inherited.allData, getData(route)), outlet, route.component, route,
getSourceSegment(rawSegment), getPathIndexShift(rawSegment) - 1, newInheritedResolve); getSourceSegment(rawSegment), getPathIndexShift(rawSegment) + paths.length,
newInheritedResolve);
return [new TreeNode<ActivatedRouteSnapshot>(snapshot, [])]; return [new TreeNode<ActivatedRouteSnapshot>(snapshot, [])];
} }
@ -130,7 +131,7 @@ function processPathsWithParamsAgainstRoute(
const snapshot = new ActivatedRouteSnapshot( const snapshot = new ActivatedRouteSnapshot(
consumedPaths, Object.freeze(merge(inherited.allParams, parameters)), consumedPaths, Object.freeze(merge(inherited.allParams, parameters)),
merge(inherited.allData, getData(route)), outlet, route.component, route, merge(inherited.allData, getData(route)), outlet, route.component, route,
getSourceSegment(rawSegment), getPathIndexShift(rawSegment) + pathIndex + lastChild - 1, getSourceSegment(rawSegment), getPathIndexShift(rawSegment) + consumedPaths.length,
newInheritedResolve); newInheritedResolve);
const newInherited = route.component ? const newInherited = route.component ?
@ -227,12 +228,12 @@ function getSourceSegment(segment: UrlSegment): UrlSegment {
function getPathIndexShift(segment: UrlSegment): number { function getPathIndexShift(segment: UrlSegment): number {
let s = segment; let s = segment;
let res = 0; let res = (s._pathIndexShift ? s._pathIndexShift : 0);
while (s._sourceSegment) { while (s._sourceSegment) {
s = s._sourceSegment; s = s._sourceSegment;
res += segment._pathIndexShift; res += (s._pathIndexShift ? s._pathIndexShift : 0);
} }
return res; return res - 1;
} }
function split( function split(
@ -245,7 +246,7 @@ function split(
createChildrenForEmptyPaths( createChildrenForEmptyPaths(
segment, consumedPaths, config, new UrlSegment(slicedPath, segment.children))); segment, consumedPaths, config, new UrlSegment(slicedPath, segment.children)));
s._sourceSegment = segment; s._sourceSegment = segment;
s._pathIndexShift = 0; s._pathIndexShift = consumedPaths.length;
return {segment: s, slicedPath: []}; return {segment: s, slicedPath: []};
} else if (slicedPath.length === 0 && containsEmptyPathMatches(segment, slicedPath, config)) { } else if (slicedPath.length === 0 && containsEmptyPathMatches(segment, slicedPath, config)) {
@ -253,11 +254,14 @@ function split(
segment.pathsWithParams, segment.pathsWithParams,
addEmptyPathsToChildrenIfNeeded(segment, slicedPath, config, segment.children)); addEmptyPathsToChildrenIfNeeded(segment, slicedPath, config, segment.children));
s._sourceSegment = segment; s._sourceSegment = segment;
s._pathIndexShift = 0; s._pathIndexShift = consumedPaths.length;
return {segment: s, slicedPath}; return {segment: s, slicedPath};
} else { } else {
return {segment, slicedPath}; const s = new UrlSegment(segment.pathsWithParams, segment.children);
s._sourceSegment = segment;
s._pathIndexShift = consumedPaths.length;
return {segment: s, slicedPath};
} }
} }

View File

@ -8,7 +8,6 @@ import {DefaultUrlSerializer, UrlPathWithParams, UrlSegment, UrlTree} from '../s
describe('createUrlTree', () => { describe('createUrlTree', () => {
const serializer = new DefaultUrlSerializer(); const serializer = new DefaultUrlSerializer();
it('should navigate to the root', () => { it('should navigate to the root', () => {
const p = serializer.parse('/'); const p = serializer.parse('/');
const t = createRoot(p, ['/']); const t = createRoot(p, ['/']);
@ -132,12 +131,6 @@ describe('createUrlTree', () => {
expect(serializer.serialize(t)).toEqual('/a/c2'); expect(serializer.serialize(t)).toEqual('/a/c2');
}); });
it('should work when given ../', () => {
const p = serializer.parse('/a/c');
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ['../', 'c2']);
expect(serializer.serialize(t)).toEqual('/a/c2');
});
it('should support setting matrix params', () => { it('should support setting matrix params', () => {
const p = serializer.parse('/a/(c//left:cp)(left:ap)'); const p = serializer.parse('/a/(c//left:cp)(left:ap)');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../', {x: 5}]); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../', {x: 5}]);
@ -152,10 +145,22 @@ describe('createUrlTree', () => {
expect(serializer.serialize(t)).toEqual('/q2(left:ap)'); expect(serializer.serialize(t)).toEqual('/q2(left:ap)');
}); });
xit('should navigate to the root', () => { it('should navigate to the root', () => {
const p = serializer.parse('/a/c'); const p = serializer.parse('/a/c');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../']); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../']);
expect(serializer.serialize(t)).toEqual(''); expect(serializer.serialize(t)).toEqual('/');
});
it('should work with ../ when absolute url', () => {
const p = serializer.parse('/a/c');
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ['../', 'c2']);
expect(serializer.serialize(t)).toEqual('/a/c2');
});
it('should work with position = -1', () => {
const p = serializer.parse('/');
const t = create(p.root, -1, p, ['11']);
expect(serializer.serialize(t)).toEqual('/11');
}); });
it('should throw when too many ..', () => { it('should throw when too many ..', () => {
@ -196,6 +201,7 @@ describe('createUrlTree', () => {
}); });
}); });
function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) { function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) {
const s = new ActivatedRouteSnapshot( const s = new ActivatedRouteSnapshot(
[], <any>{}, <any>{}, PRIMARY_OUTLET, 'someComponent', null, tree.root, -1, <any>null); [], <any>{}, <any>{}, PRIMARY_OUTLET, 'someComponent', null, tree.root, -1, <any>null);

View File

@ -81,6 +81,28 @@ describe('recognize', () => {
}); });
}); });
it('should set url segment and index properly (wildcard)', () => {
const url = tree('a/b/c');
recognize(
RootComponent,
[
{path: 'a', component: ComponentA, children: [{path: '**', component: ComponentB}]},
],
url, 'a/b/c')
.subscribe((s: RouterStateSnapshot) => {
expect(s.root._urlSegment).toBe(url.root);
expect(s.root._lastPathIndex).toBe(-1);
const compA = s.firstChild(s.root);
expect(compA._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(compA._lastPathIndex).toBe(0);
const compC = s.firstChild(<any>compA);
expect(compC._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(compC._lastPathIndex).toBe(2);
});
});
it('should match routes in the depth first order', () => { it('should match routes in the depth first order', () => {
checkRecognize( checkRecognize(
[ [
@ -357,6 +379,62 @@ describe('recognize', () => {
expect(c._lastPathIndex).toBe(0); expect(c._lastPathIndex).toBe(0);
}); });
}); });
it('should set url segment and index properly when nested empty-path segments', () => {
const url = tree('a');
recognize(
RootComponent, [{
path: 'a',
children: [
{path: '', component: ComponentB, children: [{path: '', component: ComponentC}]}
]
}],
url, 'a')
.forEach((s: RouterStateSnapshot) => {
expect(s.root._urlSegment).toBe(url.root);
expect(s.root._lastPathIndex).toBe(-1);
const a = s.firstChild(s.root);
expect(a._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(a._lastPathIndex).toBe(0);
const b = s.firstChild(a);
expect(b._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(b._lastPathIndex).toBe(0);
const c = s.firstChild(b);
expect(c._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(c._lastPathIndex).toBe(0);
});
});
it('should set url segment and index properly when nested empty-path segments (2)', () => {
const url = tree('');
recognize(
RootComponent, [{
path: '',
children: [
{path: '', component: ComponentB, children: [{path: '', component: ComponentC}]}
]
}],
url, '')
.forEach((s: RouterStateSnapshot) => {
expect(s.root._urlSegment).toBe(url.root);
expect(s.root._lastPathIndex).toBe(-1);
const a = s.firstChild(s.root);
expect(a._urlSegment).toBe(url.root);
expect(a._lastPathIndex).toBe(-1);
const b = s.firstChild(a);
expect(b._urlSegment).toBe(url.root);
expect(b._lastPathIndex).toBe(-1);
const c = s.firstChild(b);
expect(c._urlSegment).toBe(url.root);
expect(c._lastPathIndex).toBe(-1);
});
});
}); });
describe('aux split at the end (no right child)', () => { describe('aux split at the end (no right child)', () => {