fix(router): update getRouteGuards to check if the context outlet is activated (#39049)

In certain circumstances (errors during component constructor) the router outlet may not be activated before
redirecting to a new route. If the new route requires running guards and resolvers the current logic will throw
when accessing outlet.component due to an isActivated check within the property getter.  This update brings the
logic inline with deactivateRouterAndItsChildren, namely checking outlet.isActivated before trying to access
outlet.component.

Fixes #39030

PR Close #39049
This commit is contained in:
Zack Birkenbuel 2020-09-29 09:10:11 -07:00 committed by Joey Perrott
parent 5b15e5eeae
commit ea1968317e
2 changed files with 18 additions and 3 deletions

View File

@ -121,9 +121,8 @@ function getRouteGuards(
getChildRouteGuards(futureNode, currNode, parentContexts, futurePath, checks); getChildRouteGuards(futureNode, currNode, parentContexts, futurePath, checks);
} }
if (shouldRun) { if (shouldRun && context && context.outlet && context.outlet.isActivated) {
const component = context && context.outlet && context.outlet.component || null; checks.canDeactivateChecks.push(new CanDeactivate(context.outlet.component, curr));
checks.canDeactivateChecks.push(new CanDeactivate(component, curr));
} }
} else { } else {
if (curr) { if (curr) {

View File

@ -3027,6 +3027,13 @@ describe('Integration', () => {
resolve: {data: 'resolver'}, resolve: {data: 'resolver'},
}, },
] ]
},
{
path: 'throwing',
runGuardsAndResolvers,
component: ThrowingCmp,
canActivate: ['guard'],
resolve: {data: 'resolver'}
} }
]); ]);
@ -3125,6 +3132,15 @@ describe('Integration', () => {
advance(fixture); advance(fixture);
expect(guardRunCount).toEqual(5); expect(guardRunCount).toEqual(5);
expect(recordedData).toEqual([{data: 0}, {data: 1}, {data: 2}, {data: 3}, {data: 4}]); expect(recordedData).toEqual([{data: 0}, {data: 1}, {data: 2}, {data: 3}, {data: 4}]);
// Issue #39030, always running guards and resolvers should not throw
// when navigating away from a component with a throwing constructor.
expect(() => {
router.navigateByUrl('/throwing').catch(() => {});
advance(fixture);
router.navigateByUrl('/a;p=1');
advance(fixture);
}).not.toThrow();
}))); })));
it('should rerun rerun guards and resolvers when path params change', it('should rerun rerun guards and resolvers when path params change',