fix(router): Fix relative link generation from empty path components (#37446)

Partial resubmit of #26243
Fixes incorrect url tree generation for empty path components with children.
Adds a test to demonstrate the failure of createUrlTree for those routes.
Fixes #13011
Fixes #35687

PR Close #37446
This commit is contained in:
Andrew Scott 2020-06-08 09:26:16 -07:00 committed by atscott
parent 946f1179e9
commit 8d817daf78
2 changed files with 32 additions and 4 deletions

View File

@ -148,7 +148,12 @@ function findStartingPosition(nav: Navigation, tree: UrlTree, route: ActivatedRo
} }
if (route.snapshot._lastPathIndex === -1) { if (route.snapshot._lastPathIndex === -1) {
return new Position(route.snapshot._urlSegment, true, 0); const segmentGroup = route.snapshot._urlSegment;
// Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
// see issue #26224, #13011, #35687
// However, if the ActivatedRoute is the root we should process children like above.
const processChildren = segmentGroup === tree.root;
return new Position(segmentGroup, processChildren, 0);
} }
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1; const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;

View File

@ -11,7 +11,7 @@ import {BehaviorSubject} from 'rxjs';
import {createUrlTree} from '../src/create_url_tree'; import {createUrlTree} from '../src/create_url_tree';
import {ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute} from '../src/router_state'; import {ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute} from '../src/router_state';
import {Params, PRIMARY_OUTLET} from '../src/shared'; import {Params, PRIMARY_OUTLET} from '../src/shared';
import {DefaultUrlSerializer, UrlSegmentGroup, UrlTree} from '../src/url_tree'; import {DefaultUrlSerializer, UrlSegment, UrlSegmentGroup, UrlTree} from '../src/url_tree';
describe('createUrlTree', () => { describe('createUrlTree', () => {
const serializer = new DefaultUrlSerializer(); const serializer = new DefaultUrlSerializer();
@ -240,6 +240,29 @@ describe('createUrlTree', () => {
const t = createRoot(p, [], {}, 'fragment'); const t = createRoot(p, [], {}, 'fragment');
expect(t.fragment).toEqual('fragment'); expect(t.fragment).toEqual('fragment');
}); });
it('should support pathless route', () => {
const p = serializer.parse('/a');
const t = create(p.root.children[PRIMARY_OUTLET], -1, p, ['b']);
expect(serializer.serialize(t)).toEqual('/b');
});
it('should support pathless route with ../ at root', () => {
const p = serializer.parse('/a');
const t = create(p.root.children[PRIMARY_OUTLET], -1, p, ['../b']);
expect(serializer.serialize(t)).toEqual('/b');
});
it('should support pathless child of pathless root', () => {
// i.e. routes = {path: '', loadChildren: () => import('child')...}
// forChild: {path: '', component: Comp}
const p = serializer.parse('');
const empty = new UrlSegmentGroup([], {});
p.root.children[PRIMARY_OUTLET] = empty;
empty.parent = p.root;
const t = create(empty, -1, p, ['lazy']);
expect(serializer.serialize(t)).toEqual('/lazy');
});
}); });
function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) { function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) {
@ -260,8 +283,8 @@ function create(
expect(segment).toBeDefined(); expect(segment).toBeDefined();
} }
const s = new (ActivatedRouteSnapshot as any)( const s = new (ActivatedRouteSnapshot as any)(
[], <any>{}, <any>{}, '', <any>{}, PRIMARY_OUTLET, 'someComponent', null, <any>segment, segment.segments, <any>{}, <any>{}, '', <any>{}, PRIMARY_OUTLET, 'someComponent', null,
startIndex, <any>null); <any>segment, startIndex, <any>null);
const a = new (ActivatedRoute as any)( const a = new (ActivatedRoute as any)(
new BehaviorSubject(null!), new BehaviorSubject(null!), new BehaviorSubject(null!), new BehaviorSubject(null!), new BehaviorSubject(null!), new BehaviorSubject(null!),
new BehaviorSubject(null!), new BehaviorSubject(null!), PRIMARY_OUTLET, 'someComponent', s); new BehaviorSubject(null!), new BehaviorSubject(null!), PRIMARY_OUTLET, 'someComponent', s);