Dynamic ways to conditionally lazy load a component in Angular
2 min readFeb 8, 2023
Use the Route factory (to utilize the DI)
import { ROUTES } from '@angular/router';
providers: [
{
provide: ROUTES,
useFactory: () => {
const myService = inject(MyService);
return getRoutes(myService); // using your service variables to manipulate routes
}
}
]
Manipulate loadComponent
or loadChildren
getRoutes = (myService) =>
[
{
path: 'samples',
loadComponent: () =>
myService.mode === 'v1'
? import('./samples-v1.module') // module having default export
: import('./samples-v2.module') // module having default export
}
]
or Manipulate the matcher
getRoutes = (myService) =>
[
{
matcher: (urlSegment: UrlSegment[]) => {
return myService.mode === 'v1' && url[0].path === 'samples'
? { consumed: url }
: null;
},
loadComponent: () => import('./samples-v1.module')
},
{
matcher: (urlSegment: UrlSegment[]) => {
return myService.mode === 'v2' && url[0].path === 'samples'
? { consumed: url }
: null;
},
loadComponent: () => import('./samples-v2.module')
}
]
Alternatively, you can consume a map instead of if-else conditions
componentsMap = {
v1: () => import('./samples-v1.module'), // module having default export
v2: () => import('./samples-v2.module').then(m => m.SampleV2Component) // not default export
}
export class UtilService {
loadComponent(version) {
return componentsMap[version]();
}
}
or Load your component in the resolver
// Route
{
path: 'samples',
component: SamplesContainerComponent
resolve: {
componentToBeInstantiated: resolveComponentFn,
},
}
// resolveComponentFn
const resolveComponentFn: ResolveFn<unknown> = (route) => {
const myService = inject(MyService);
const utilSerive = inject(UtilService);
return lastValueFrom(
myService.getVersion(route.queryParamMap.get('platform'))
).then(v => {
return utilService.loadComponent(v);
})
}
Using the resolved component in the wrapper component
<-- SamplesContainerComponent template -->
<section>
<ng-container
*ngComponentOutlet="
componentToBeInstantiated;
content: [[footer]]
">
</ng-container>
</section>
<footer #footer>Content inside footer</footer>
or Instantiating your lazy component through router-outlet
@Component({
template: `<router-outlet></router-outlet>`
})
export class SamplesContainerComponent implements OnInit {
@ViewChild(RouterOutlet) routerOutlet: RouterOutlet;
private readonly myService = inject(MyService);
ngOnInit(): void {
this.myService.getRequiredComponent().pipe(untilDestroyed(this))
.subscribe((compClass) => {
this.routerOutlet.attach(
this.viewContainerRef.createComponent(compClass),
this.route
);
});
}
}
Use your lazy “standalone” component in Dialog
openDialog(v) {
this.utilService.loadComponent(v).then(m => {
this.matDialog.open(m.MyComponent, {
id: 'call',
disableClose: true,
data: {type: 'video'}
});
});
}
You can use the same strategies even to load your remote components or modules using the module federation plugin
Thanks for being along, show some applause 👏 if liked the content and stay tuned for more of such content