This was harder to find than I expected. There are a lot of answers around about dynamically rendering a component which is already loaded. There are enough docs to understand lazy loading routed modules. However lazy loading a module without a route and get a component instance, which you can later render dynamically is somewhat tricky but it can be done.
The trick is to provide a resolver service within the module and use ViewContainerRef from the 'parent' component to create the component you need.
Here is the module definition which exposes the resolver service:
@NgModule({ declarations: [ OnboardingGuideComponent, ... ], imports: [ CommonModule, ... ], providers: [OnboardingResolverService], }) export class OnboardingModule { public static getProviders(): Array<any> { return [OnboardingResolverService]; } }
Here is the OnboardingResolverService:
@Injectable({ providedIn: 'root' }) export class OnboardingResolverService { public resolveComponent(vcr: ViewContainerRef): ComponentRef<OnboardingGuideComponent> { return vcr.createComponent(OnboardingGuideComponent); } }
And here is the method in app.component which actually loads the module and get the component instance which can be later rendered dynamically:
@ViewChild('dynamicComponent', { read: ViewContainerRef, static: true }) vcr!: ViewContainerRef; /** ........... */ private async loadOnboarding(){ const { OnboardingModule } = await import('./onboarding/onboarding.module'); const moduleRef = Injector.create({providers: OnboardingModule.getProviders()}); const onboardingResolver = moduleRef.get(OnboardingResolverService); const componentRef = onboardingResolver.resolveComponent(this.vcr); this.dcr = { comp: componentRef.componentType, /** my implementation of dynamic renderer expects a Type, yours can expect a component instance */ data: {} }; }
Anyways, this seems to be working for now. Credit for the idea goes to https://medium.com/@ckyidr9/lazy-load-feature-modules-without-routing-in-angular-9-ivy-220851cc7751