fix(router): prevent calling unsubscribe on undefined subscription in RouterPreloader (#38344)

Previously, the `ngOnDestroy` method called `unsubscribe` regardless of if `subscription` had
been initialized.  This can lead to an error attempting to call `unsubscribe` of undefined.
This change prevents this error, and instead only attempts `unsubscribe` when the subscription
has been defined.

PR Close #38344
This commit is contained in:
Joey Perrott 2020-08-04 10:56:39 -07:00 committed by Andrew Kushnir
parent ba175be41f
commit 763023472b
2 changed files with 21 additions and 6 deletions

View File

@ -74,8 +74,7 @@ export class NoPreloading implements PreloadingStrategy {
@Injectable() @Injectable()
export class RouterPreloader implements OnDestroy { export class RouterPreloader implements OnDestroy {
private loader: RouterConfigLoader; private loader: RouterConfigLoader;
// TODO(issue/24571): remove '!'. private subscription?: Subscription;
private subscription!: Subscription;
constructor( constructor(
private router: Router, moduleLoader: NgModuleFactoryLoader, compiler: Compiler, private router: Router, moduleLoader: NgModuleFactoryLoader, compiler: Compiler,
@ -98,12 +97,11 @@ export class RouterPreloader implements OnDestroy {
return this.processRoutes(ngModule, this.router.config); return this.processRoutes(ngModule, this.router.config);
} }
// TODO(jasonaden): This class relies on code external to the class to call setUpPreloading. If
// this hasn't been done, ngOnDestroy will fail as this.subscription will be undefined. This
// should be refactored.
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
}
private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> { private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> {
const res: Observable<any>[] = []; const res: Observable<any>[] = [];

View File

@ -19,6 +19,23 @@ describe('RouterPreloader', () => {
class LazyLoadedCmp { class LazyLoadedCmp {
} }
describe('should properly handle', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes(
[{path: 'lazy', loadChildren: 'expected', canLoad: ['someGuard']}])],
providers: [{provide: PreloadingStrategy, useExisting: PreloadAllModules}]
});
});
it('being destroyed before expected', () => {
const preloader: RouterPreloader = TestBed.get(RouterPreloader);
// Calling the RouterPreloader's ngOnDestroy method is done to simulate what would happen if
// the containing NgModule is destroyed.
expect(() => preloader.ngOnDestroy()).not.toThrow();
});
});
describe('should not load configurations with canLoad guard', () => { describe('should not load configurations with canLoad guard', () => {
@NgModule({ @NgModule({
declarations: [LazyLoadedCmp], declarations: [LazyLoadedCmp],