( segmentGroup: UrlSegmentGroup, startIndex: number, commands: readonly any[], )
| 412 | } |
| 413 | |
| 414 | function updateSegmentGroupChildren( |
| 415 | segmentGroup: UrlSegmentGroup, |
| 416 | startIndex: number, |
| 417 | commands: readonly any[], |
| 418 | ): UrlSegmentGroup { |
| 419 | if (commands.length === 0) { |
| 420 | return new UrlSegmentGroup(segmentGroup.segments, {}); |
| 421 | } else { |
| 422 | const outlets = getOutlets(commands); |
| 423 | // Keyed by outlet name, which can be `__proto__`, so use a null-prototype map. |
| 424 | const children: {[key: string]: UrlSegmentGroup} = Object.create(null); |
| 425 | // If the set of commands applies to anything other than the primary outlet and the child |
| 426 | // segment is an empty path primary segment on its own, we want to apply the commands to the |
| 427 | // empty child path rather than here. The outcome is that the empty primary child is effectively |
| 428 | // removed from the final output UrlTree. Imagine the following config: |
| 429 | // |
| 430 | // {path: '', children: [{path: '**', outlet: 'popup'}]}. |
| 431 | // |
| 432 | // Navigation to /(popup:a) will activate the child outlet correctly Given a follow-up |
| 433 | // navigation with commands |
| 434 | // ['/', {outlets: {'popup': 'b'}}], we _would not_ want to apply the outlet commands to the |
| 435 | // root segment because that would result in |
| 436 | // //(popup:a)(popup:b) since the outlet command got applied one level above where it appears in |
| 437 | // the `ActivatedRoute` rather than updating the existing one. |
| 438 | // |
| 439 | // Because empty paths do not appear in the URL segments and the fact that the segments used in |
| 440 | // the output `UrlTree` are squashed to eliminate these empty paths where possible |
| 441 | // https://github.com/angular/angular/blob/13f10de40e25c6900ca55bd83b36bd533dacfa9e/packages/router/src/url_tree.ts#L755 |
| 442 | // it can be hard to determine what is the right thing to do when applying commands to a |
| 443 | // `UrlSegmentGroup` that is created from an "unsquashed"/expanded `ActivatedRoute` tree. |
| 444 | // This code effectively "squashes" empty path primary routes when they have no siblings on |
| 445 | // the same level of the tree. |
| 446 | if ( |
| 447 | Object.keys(outlets).some((o) => o !== PRIMARY_OUTLET) && |
| 448 | segmentGroup.children[PRIMARY_OUTLET] && |
| 449 | segmentGroup.numberOfChildren === 1 && |
| 450 | segmentGroup.children[PRIMARY_OUTLET].segments.length === 0 |
| 451 | ) { |
| 452 | const childrenOfEmptyChild = updateSegmentGroupChildren( |
| 453 | segmentGroup.children[PRIMARY_OUTLET], |
| 454 | startIndex, |
| 455 | commands, |
| 456 | ); |
| 457 | return new UrlSegmentGroup(segmentGroup.segments, childrenOfEmptyChild.children); |
| 458 | } |
| 459 | |
| 460 | Object.entries(outlets).forEach(([outlet, commands]) => { |
| 461 | if (typeof commands === 'string') { |
| 462 | commands = [commands]; |
| 463 | } |
| 464 | if (commands !== null) { |
| 465 | children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands); |
| 466 | } |
| 467 | }); |
| 468 | |
| 469 | Object.entries(segmentGroup.children).forEach(([childOutlet, child]) => { |
| 470 | if (outlets[childOutlet] === undefined) { |
| 471 | children[childOutlet] = child; |
no test coverage detected