From 5ae6915600ab52d1c4a47bc8bdcde30b9ef323c1 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 19 Oct 2016 12:23:00 -0700 Subject: [PATCH] fix(router): fix lazy loading triggered by redirects from wildcard routes Closes #12183 --- .../@angular/router/src/apply_redirects.ts | 12 ++++---- .../router/test/apply_redirects.spec.ts | 28 +++++++++++++++++++ .../@angular/router/test/integration.spec.ts | 28 +++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/modules/@angular/router/src/apply_redirects.ts b/modules/@angular/router/src/apply_redirects.ts index 0c95d2cc88..b66afa0a5f 100644 --- a/modules/@angular/router/src/apply_redirects.ts +++ b/modules/@angular/router/src/apply_redirects.ts @@ -159,7 +159,6 @@ class ApplyRedirects { if (getOutlet(route) !== outlet) return noMatch(segmentGroup); if (route.redirectTo !== undefined && !(allowRedirects && this.allowRedirects)) return noMatch(segmentGroup); - if (route.redirectTo === undefined) { return this.matchSegmentAgainstRoute(injector, segmentGroup, route, paths); } else { @@ -172,20 +171,23 @@ class ApplyRedirects { injector: Injector, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route, segments: UrlSegment[], outlet: string): Observable { if (route.path === '**') { - return this.expandWildCardWithParamsAgainstRouteUsingRedirect(route); + return this.expandWildCardWithParamsAgainstRouteUsingRedirect( + injector, routes, route, outlet); } else { return this.expandRegularSegmentAgainstRouteUsingRedirect( injector, segmentGroup, routes, route, segments, outlet); } } - private expandWildCardWithParamsAgainstRouteUsingRedirect(route: Route): - Observable { + private expandWildCardWithParamsAgainstRouteUsingRedirect( + injector: Injector, routes: Route[], route: Route, + outlet: string): Observable { const newSegments = applyRedirectCommands([], route.redirectTo, {}); if (route.redirectTo.startsWith('/')) { return absoluteRedirect(newSegments); } else { - return of (new UrlSegmentGroup(newSegments, {})); + const group = new UrlSegmentGroup(newSegments, {}); + return this.expandSegment(injector, group, routes, newSegments, outlet, false); } } diff --git a/modules/@angular/router/test/apply_redirects.spec.ts b/modules/@angular/router/test/apply_redirects.spec.ts index 814e87cd90..c7a2c3a38b 100644 --- a/modules/@angular/router/test/apply_redirects.spec.ts +++ b/modules/@angular/router/test/apply_redirects.spec.ts @@ -305,6 +305,34 @@ describe('applyRedirects', () => { expect((config[0])._loadedConfig).toBe(loadedConfig); }); }); + + it('should load the configuration after a local redirect from a wildcard route', () => { + const loadedConfig = new LoadedRouterConfig( + [{path: '', component: ComponentB}], 'stubInjector', 'stubFactoryResolver'); + + const loader = {load: (injector: any, p: any) => of (loadedConfig)}; + + const config = + [{path: 'not-found', loadChildren: 'children'}, {path: '**', redirectTo: 'not-found'}]; + + applyRedirects('providedInjector', loader, tree('xyz'), config).forEach(r => { + expect((config[0])._loadedConfig).toBe(loadedConfig); + }); + }); + + it('should load the configuration after an absolute redirect from a wildcard route', () => { + const loadedConfig = new LoadedRouterConfig( + [{path: '', component: ComponentB}], 'stubInjector', 'stubFactoryResolver'); + + const loader = {load: (injector: any, p: any) => of (loadedConfig)}; + + const config = + [{path: 'not-found', loadChildren: 'children'}, {path: '**', redirectTo: '/not-found'}]; + + applyRedirects('providedInjector', loader, tree('xyz'), config).forEach(r => { + expect((config[0])._loadedConfig).toBe(loadedConfig); + }); + }); }); describe('empty paths', () => { diff --git a/modules/@angular/router/test/integration.spec.ts b/modules/@angular/router/test/integration.spec.ts index f9af0d3c99..33cfbde3d8 100644 --- a/modules/@angular/router/test/integration.spec.ts +++ b/modules/@angular/router/test/integration.spec.ts @@ -1562,6 +1562,7 @@ describe('Integration', () => { .toEqual( `RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`); }))); + it('should combine routes from multiple modules into a single configuration', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], @@ -1705,6 +1706,33 @@ describe('Integration', () => { [[NavigationStart, '/lazy/loaded'], [NavigationError, '/lazy/loaded']]); }))); + it('should work with complex redirect rules', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({selector: 'lazy', template: 'lazy-loaded'}) + class LazyLoadedComponent { + } + + @NgModule({ + declarations: [LazyLoadedComponent], + imports: [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])], + }) + class LoadedModule { + } + + loader.stubbedModules = {lazy: LoadedModule}; + const fixture = createRoot(router, RootCmp); + + router.resetConfig( + [{path: 'lazy', loadChildren: 'lazy'}, {path: '**', redirectTo: 'lazy'}]); + + router.navigateByUrl('/lazy/loaded'); + advance(fixture); + + expect(location.path()).toEqual('/lazy/loaded'); + }))); + describe('preloading', () => { beforeEach(() => { TestBed.configureTestingModule(