Skip to content

Commit 0d2aa76

Browse files
feat: add support for lazy loading NgModules
BREAKING CHANGE: The input for lazy loading has change from [loadComponent] to [load] to support components and NgModules BEFORE: <route path="/path" [loadComponent]=""></route> AFTER: <route path="/path" [load]=""></route>
1 parent 5c0b345 commit 0d2aa76

File tree

6 files changed

+68
-43
lines changed

6 files changed

+68
-43
lines changed

apps/example-app/src/app/app.module.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ import { CoreModule } from '@example-app/core';
1818
import { UserEffects, RouterEffects } from '@example-app/core/effects';
1919
import { AppComponent } from '@example-app/core/containers';
2020
import * as fromAuth from '@example-app/auth/reducers';
21-
import { BookEffects, CollectionEffects } from '@example-app/books/effects';
22-
23-
import * as fromBooks from '@example-app/books/reducers';
2421

2522
@NgModule({
2623
imports: [
@@ -78,24 +75,6 @@ import * as fromBooks from '@example-app/books/reducers';
7875
* See: https://ngrx.io/guide/effects#registering-root-effects
7976
*/
8077
EffectsModule.forRoot([UserEffects]),
81-
82-
/**
83-
* StoreModule.forFeature is used for composing state
84-
* from feature modules. These modules can be loaded
85-
* eagerly or lazily and will be dynamically added to
86-
* the existing state.
87-
*/
88-
StoreModule.forFeature(fromBooks.booksFeatureKey, fromBooks.reducers),
89-
90-
/**
91-
* Effects.forFeature is used to register effects
92-
* from feature modules. Effects can be loaded
93-
* eagerly or lazily and will be started immediately.
94-
*
95-
* All Effects will only be instantiated once regardless of
96-
* whether they are registered once or multiple times.
97-
*/
98-
EffectsModule.forFeature([BookEffects, CollectionEffects]),
9978
CoreModule,
10079
],
10180
bootstrap: [AppComponent],

apps/example-app/src/app/books/books.module.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import {
2020

2121
import { MaterialModule } from '@example-app/material';
2222
import { PipesModule } from '@example-app/shared/pipes';
23-
import { RoutingModule } from 'angular-routing';
23+
import { RoutingModule, ModuleWithRoute } from 'angular-routing';
2424
import { AuthGuard } from '@example-app/auth/services';
25+
import { BookEffects, CollectionEffects } from '@example-app/books/effects';
2526

27+
import * as fromBooks from '@example-app/books/reducers';
2628

2729
@Component({
28-
selector: 'app-books',
30+
selector: 'bc-books',
2931
template: `
3032
<router *ngIf="loggedIn$ | async">
3133
<route path="/find">
@@ -69,8 +71,27 @@ export const CONTAINERS = [
6971
MaterialModule,
7072
RoutingModule,
7173
PipesModule,
74+
/**
75+
* StoreModule.forFeature is used for composing state
76+
* from feature modules. These modules can be loaded
77+
* eagerly or lazily and will be dynamically added to
78+
* the existing state.
79+
*/
80+
StoreModule.forFeature(fromBooks.booksFeatureKey, fromBooks.reducers),
81+
82+
/**
83+
* Effects.forFeature is used to register effects
84+
* from feature modules. Effects can be loaded
85+
* eagerly or lazily and will be started immediately.
86+
*
87+
* All Effects will only be instantiated once regardless of
88+
* whether they are registered once or multiple times.
89+
*/
90+
EffectsModule.forFeature([BookEffects, CollectionEffects]),
7291
],
7392
declarations: [COMPONENTS, CONTAINERS],
7493
entryComponents: [BooksComponent]
7594
})
76-
export class BooksModule {}
95+
export class BooksModule implements ModuleWithRoute {
96+
routeComponent = BooksComponent;
97+
}

apps/example-app/src/app/core/containers/app.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { LayoutActions } from '@example-app/core/actions';
4343
</bc-toolbar>
4444
4545
<router>
46-
<route path="/books/**" [loadComponent]="components.books"></route>
46+
<route path="/books/**" [load]="components.books"></route>
4747
<route path="/login">
4848
<bc-login-page *routeComponent></bc-login-page>
4949
</route>
@@ -58,7 +58,7 @@ import { LayoutActions } from '@example-app/core/actions';
5858
})
5959
export class AppComponent {
6060
components = {
61-
books: () => import('../../books/books.module').then(m => m.BooksComponent)
61+
books: () => import('../../books/books.module').then(m => m.BooksModule)
6262
};
6363
showSidenav$: Observable<boolean>;
6464
loggedIn$: Observable<boolean>;

libs/angular-routing/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-routing",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "A declarative router for Angular applications",
55
"repository": {
66
"type": "git",

libs/angular-routing/src/lib/route.component.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import {
99
TemplateRef,
1010
ChangeDetectionStrategy,
1111
Self,
12+
NgModuleFactory,
13+
Compiler,
1214
} from '@angular/core';
1315

14-
import { Subject, BehaviorSubject, merge, of } from 'rxjs';
16+
import { Subject, BehaviorSubject, merge, of, from } from 'rxjs';
1517
import {
1618
distinctUntilChanged,
1719
filter,
@@ -20,7 +22,7 @@ import {
2022
withLatestFrom,
2123
} from 'rxjs/operators';
2224

23-
import { LoadComponent, Route } from './route';
25+
import { Load, Route } from './route';
2426
import { Params, RouteParams } from './route-params.service';
2527
import { RouterComponent } from './router.component';
2628
import { Router } from './router.service';
@@ -49,7 +51,7 @@ export class RouteComponent implements OnInit {
4951
@ContentChild(TemplateRef) template: TemplateRef<any> | null;
5052
@Input() path: string;
5153
@Input() component: Type<any>;
52-
@Input() loadComponent: LoadComponent;
54+
@Input() load: Load;
5355
@Input() reuse = true;
5456
@Input() redirectTo!: string;
5557

@@ -65,7 +67,8 @@ export class RouteComponent implements OnInit {
6567
private router: Router,
6668
private routerComponent: RouterComponent,
6769
private resolver: ComponentFactoryResolver,
68-
private viewContainerRef: ViewContainerRef
70+
private viewContainerRef: ViewContainerRef,
71+
private compiler: Compiler
6972
) {}
7073

7174
ngOnInit(): void {
@@ -76,7 +79,7 @@ export class RouteComponent implements OnInit {
7679

7780
this.route = this.routerComponent.registerRoute({
7881
path,
79-
loadComponent: this.loadComponent,
82+
load: this.load,
8083
});
8184

8285
const activeRoute$ = this.routerComponent.activeRoute$.pipe(
@@ -99,6 +102,8 @@ export class RouteComponent implements OnInit {
99102

100103
return this.loadAndRender(current.route);
101104
}
105+
106+
return of(null);
102107
} else if (rendered) {
103108
return of(this.clearView());
104109
}
@@ -115,12 +120,30 @@ export class RouteComponent implements OnInit {
115120
}
116121

117122
private loadAndRender(route: Route) {
118-
if (route.loadComponent) {
119-
return route.loadComponent().then((component) => {
120-
return this.renderComponent(component);
121-
});
123+
if (route.load) {
124+
return from(route.load().then(componentOrModule => {
125+
if (componentOrModule instanceof NgModuleFactory) {
126+
const moduleRef = componentOrModule.create(this.viewContainerRef.injector);
127+
const component = moduleRef.instance.routeComponent;
128+
129+
this.renderComponent(component);
130+
} else if (componentOrModule.ɵmod) {
131+
return this.compiler.compileModuleAsync(componentOrModule as Type<any>).then(moduleFactory => {
132+
const moduleRef = moduleFactory.create(this.viewContainerRef.injector);
133+
const component = moduleRef.instance.routeComponent;
134+
this.renderComponent(component);
135+
136+
return true;
137+
});
138+
} else {
139+
this.renderComponent(componentOrModule);
140+
}
141+
142+
return true;
143+
}));
122144
} else {
123-
return of(this.showTemplate());
145+
this.showTemplate();
146+
return of(true);
124147
}
125148
}
126149

@@ -133,8 +156,6 @@ export class RouteComponent implements OnInit {
133156
this.viewContainerRef.length,
134157
this.viewContainerRef.injector
135158
);
136-
137-
return of(true);
138159
}
139160

140161
private clearComponent() {
@@ -153,7 +174,7 @@ export class RouteComponent implements OnInit {
153174
}
154175

155176
private clearView() {
156-
if (this.loadComponent) {
177+
if (this.load) {
157178
this.clearComponent();
158179
} else {
159180
this.hideTemplate();
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
import { Type } from '@angular/core';
1+
import { Type, NgModuleFactory } from '@angular/core';
22

33
import { Params } from './route-params.service';
44

5-
export type LoadComponent = () => Promise<Type<any>>;
5+
export type Load = () => Promise<NgModuleFactory<any>|Type<any>|any>;
66

77
export interface Route {
88
path: string;
99
// component?: Type<any>;
10-
loadComponent?: LoadComponent;
10+
load?: Load;
1111
matcher?: RegExp;
1212
}
1313

1414
export interface ActiveRoute {
1515
route: Route;
1616
params: Params;
1717
}
18+
19+
export interface ModuleWithRoute {
20+
routeComponent: Type<any>;
21+
}

0 commit comments

Comments
 (0)