I am facing a problem while trying to load a dynamic component from a lazy loaded module. More precisely, I am trying to open login component in a modal (https://ng-bootstrap.github.io/#/components/modal/examples) rather then in a separate page while also keeping separate login route/page intact.
Now, one option it seems to me that I have to eager load the login component in app module which is nothing more than a temporary hack. So, I don't want this solution.
I have tried directly using the component but as I anticipated, it gave me the following error:
No component factory found for SigninComponent. Did you add it to @NgModule.entryComponents?
I searched and found this link: https://github.com/angular/angular/issues/14324 but can't make it work.
I am surprised that I could not find a proper solution to this issue as this is a very common use case. Any help or suggestions are appreciated. Thank you!
Components that are not directly used in templates via their selector have to be added in a module's entryComponents
array in the @NgModule
decorator. When opening a modal in bootstrap you don't actually use the component's selector anywhere, so your SignInComponent is one such example.
You would do something like this
@NgModule({
//...
declarations: [
SignInComponent,
],
entryComponents: [
SignInComponent,
]
})
export class LazyLoadedModule {
}
to mark a component for compilation even though it is not referenced in any other component's template via its selector.
You can read more about entry components, how they work, and why you need to declare them this way in the official angular docs here. As well as this StackOverflow post.
Here's how I managed to do what you want to do. The idea is that, since the login component is in a lazy module, since lazy modules are loaded by the router when navigating to some routes, you need to navigate, inside the modal, to a route that loads the lazy module and shows the login component. And this can be done using a named router outlet. I'm not very experienced with named router outlets, so there might be some things to refine, but it seems to work.
So, assuming you have a lazy module LoginModule
, with an empty-path route displaying the login component, here's how the root module's routes can be defined:
export const ROUTES: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'login',
loadChildren: './login/login.module#LoginModule'
},
{
path: 'modal-login',
component: ModalLoginShellComponent,
outlet: 'modal',
children: [
{
path: '',
loadChildren: './login/login.module#LoginModule'
}
]
}
];
The home component would have a link allowing to open a ModalLoginComponent inside a modal (as shown in the ng-bootstrap example). This ModalLoginComponent template would look like this:
<div class="modal-header">
<h4 class="modal-title">Hi there!</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<router-outlet name="modal"></router-outlet>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>
The important part being
<router-outlet name="modal"></router-outlet>
It allows, inside the modal, to navigate to a route, and in particular, to a route that would load the lazy module.
The code of the ModalLoginComponent would have the following ngOnInit(), which would trigger the navigation:
ngOnInit() {
this.router.navigate([{outlets: {'modal': ['modal-login']}}]);
}
This would thus load the ModalLoginShellComponent and its default lazy-loaded child route inside the body of the modal. The ModalLoginShellComponent is a stupid component doing nothing, and just having, as its template
<router-outlet></router-outlet>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With