Dynamic ways to conditionally lazy load a component in Angular

Ankit kaushik
2 min readFeb 8, 2023
Photo by Priscilla Du Preez on Unsplash

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

--

--