From b203e4c19d4ccec09b9d1910dbc6f314070c1428 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 3 Apr 2023 13:49:45 -0700 Subject: [PATCH] fix(router): create correct URL relative to path with empty child (#49691) The previous fix for squashing empty children didn't quite work when the existing route had segments. The result would be that the segments from the existing route were dropped from the final URL. PR Close #49691 --- packages/router/src/create_url_tree.ts | 10 +++++--- packages/router/test/create_url_tree.spec.ts | 26 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/router/src/create_url_tree.ts b/packages/router/src/create_url_tree.ts index 7e5d0c09853..1f006609825 100644 --- a/packages/router/src/create_url_tree.ts +++ b/packages/router/src/create_url_tree.ts @@ -339,8 +339,9 @@ function updateSegmentGroupChildren( const outlets = getOutlets(commands); const children: {[key: string]: UrlSegmentGroup} = {}; // If the set of commands does not apply anything to the primary outlet and the child segment is - // an empty path primary segment on its own, we want to skip applying the commands at this - // level. Imagine the following config: + // an empty path primary segment on its own, we want to apply the commands to the empty child + // path rather than here. The outcome is that the empty primary child is effectively removed + // from the final output UrlTree. Imagine the following config: // // {path: '', children: [{path: '**', outlet: 'popup'}]}. // @@ -361,8 +362,9 @@ function updateSegmentGroupChildren( if (!outlets[PRIMARY_OUTLET] && segmentGroup.children[PRIMARY_OUTLET] && segmentGroup.numberOfChildren === 1 && segmentGroup.children[PRIMARY_OUTLET].segments.length === 0) { - return updateSegmentGroupChildren( - segmentGroup.children[PRIMARY_OUTLET], startIndex, commands); + const childrenOfEmptyChild = + updateSegmentGroupChildren(segmentGroup.children[PRIMARY_OUTLET], startIndex, commands); + return new UrlSegmentGroup(segmentGroup.segments, childrenOfEmptyChild.children); } Object.entries(outlets).forEach(([outlet, commands]) => { diff --git a/packages/router/test/create_url_tree.spec.ts b/packages/router/test/create_url_tree.spec.ts index 311eb67b3f9..62295bda4e5 100644 --- a/packages/router/test/create_url_tree.spec.ts +++ b/packages/router/test/create_url_tree.spec.ts @@ -206,6 +206,32 @@ describe('createUrlTree', async () => { expect(serializer.serialize(t)).toEqual('/parent/child(rootSecondary:rootPopup)'); }); + it('works with named children of empty path primary, relative to non-empty parent', + async () => { + router.resetConfig([{ + path: 'case', + component: class {}, + children: [ + { + path: '', + component: class {}, + children: [ + {path: 'foo', outlet: 'foo', children: []}, + ], + }, + ] + }]); + await router.navigateByUrl('/case'); + expect(router.url).toEqual('/case'); + expect(router + .createUrlTree( + [{outlets: {'foo': ['foo']}}], + // relative to the 'case' route + {relativeTo: router.routerState.root.firstChild}) + .toString()) + .toEqual('/case/(foo:foo)'); + }); + describe('absolute navigations', () => { it('with and pathless root', async () => { router.resetConfig([