fix(router): defer loading of wildcard module until needed (#38348)

Defer loading the wildcard module so that it is not loaded until
subscribed to. This fixes an issue where it was being eagerly loaded.
As an example, wildcard module loading should only occur after all other potential
matches have been exhausted. A test case for this was also added to
demonstrate the fix.

Fixes #25494

PR Close #38348
This commit is contained in:
Zach Pomerantz 2020-08-05 16:06:30 +00:00 committed by Andrew Kushnir
parent e34c33cd46
commit 8f708b561c
2 changed files with 27 additions and 6 deletions

View File

@ -7,7 +7,7 @@
*/ */
import {Injector, NgModuleRef} from '@angular/core'; import {Injector, NgModuleRef} from '@angular/core';
import {EmptyError, Observable, Observer, of} from 'rxjs'; import {defer, EmptyError, Observable, Observer, of} from 'rxjs';
import {catchError, concatAll, first, map, mergeMap, tap} from 'rxjs/operators'; import {catchError, concatAll, first, map, mergeMap, tap} from 'rxjs/operators';
import {LoadedRouterConfig, Route, Routes} from './config'; import {LoadedRouterConfig, Route, Routes} from './config';
@ -247,11 +247,12 @@ class ApplyRedirects {
segments: UrlSegment[]): Observable<UrlSegmentGroup> { segments: UrlSegment[]): Observable<UrlSegmentGroup> {
if (route.path === '**') { if (route.path === '**') {
if (route.loadChildren) { if (route.loadChildren) {
return this.configLoader.load(ngModule.injector, route) return defer(
() => this.configLoader.load(ngModule.injector, route)
.pipe(map((cfg: LoadedRouterConfig) => { .pipe(map((cfg: LoadedRouterConfig) => {
route._loadedConfig = cfg; route._loadedConfig = cfg;
return new UrlSegmentGroup(segments, {}); return new UrlSegmentGroup(segments, {});
})); })));
} }
return of(new UrlSegmentGroup(segments, {})); return of(new UrlSegmentGroup(segments, {}));

View File

@ -9,6 +9,7 @@
import {NgModuleRef} from '@angular/core'; import {NgModuleRef} from '@angular/core';
import {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
import {delay} from 'rxjs/operators';
import {applyRedirects} from '../src/apply_redirects'; import {applyRedirects} from '../src/apply_redirects';
import {LoadedRouterConfig, Route, Routes} from '../src/config'; import {LoadedRouterConfig, Route, Routes} from '../src/config';
@ -435,6 +436,25 @@ describe('applyRedirects', () => {
}); });
}); });
it('should not load the configuration of a wildcard route if there is a match', () => {
const loadedConfig = new LoadedRouterConfig([{path: '', component: ComponentB}], testModule);
const loader = jasmine.createSpyObj('loader', ['load']);
loader.load.and.returnValue(of(loadedConfig).pipe(delay(0)));
const config: Routes = [
{path: '', loadChildren: 'matchChildren'},
{path: '**', loadChildren: 'children'},
];
applyRedirects(testModule.injector, <any>loader, serializer, tree(''), config).forEach(r => {
expect(loader.load.calls.count()).toEqual(1);
expect(loader.load.calls.first().args).not.toContain(jasmine.objectContaining({
loadChildren: 'children'
}));
});
});
it('should load the configuration after a local redirect from a wildcard route', () => { it('should load the configuration after a local redirect from a wildcard route', () => {
const loadedConfig = new LoadedRouterConfig([{path: '', component: ComponentB}], testModule); const loadedConfig = new LoadedRouterConfig([{path: '', component: ComponentB}], testModule);