You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Entry Components of a Lazy Loaded NgModule are not available outside the Module #14324
[ ] bug report => search github for a similar issue or PR before submitting
[X] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior entryComponents of a lazy loaded NgModule cannot be rendered using ComponentFactoryResolver. Error message: No component factory found for {{entryComponent}}
Expected behavior entryComponents should be available just like if the module is imported
I created a simple setup, similar to what we use internally. Main component provides a method to render a Type. EntryComponent is declared as entryComponent in Page1Module. However after having loaded Page1Module, when trying to render EntryComponent via ComponentFactoryResolver, No component factory found for EntryComponent is thrown.
What is the motivation / use case for changing the behavior?
Render entryComponents of child modules at root level. Use cases for this approach are
Modals that shall be rendered on top of everything
Notifications
etc.
Please tell us about your environment:
We're using currently Angular 2.1.1 but this affects the latest version of Angular (2.4.6) as well (see plnkr).
Ok, if this is the intended behavior, how can specific tasks like the use cases above be accomplished?
I don't think that blowing up the main module is a proper solution.
Reacted by Michael Prentice, Brian Frichette, Familusi Babatunde, Lucas Basquerotto, Yanis Batura, Hlawuleka, Allan G, Yuriy Grunin, Dzhuneyt, Jacob Weisenburger and 16 more
If you want to use an entry component in MainComponent then you should provide it in AppModule
Reacted by Mick Lawitzke
Reacted by Joao Grassi, nuoke, William Lahti, Leo Sandström, Benjamin Mercier, asequeir-public, oky1, Mauricio Jorge Cordeiro Garrido, prasannabalajims, ARASH GHOREYSHI and 140 more
Reacted by angular-bull, José G. Ramírez, Anand Kacha, ashitosh31, David Galindo, Alex, Yaser Aslam Kazi, Roshan Bhesal - CitiusTech, Sergey Bogush, Yevhenii Chubar and 4 more
Reacted by Adrian Gallus, avin-shum, Eduard Kolosovskyi, 0x70b1a5, Kevin De Baerdemaeker, William Lahti, Leo Sandström, Ed Sinek, Alex Burykin, Roma Prots and 15 more
So your solution indeed is to move everything into the main module?
IMO this totally breaks the module concept of Angular which should allow to group functionality blocks into one module.
This means basically that things like modals are impossible to use with a lazy load architecture.
Reacted by Adrian Gallus, avin-shum, Serhii Sol, Eduard Kolosovskyi, Zéfling, Jared Grabill, Marcin Tomczyk, Joao Grassi, Josh Rathke, Leo Sandström and 104 more
Reacted by nuoke, Peter Müller, Serhii Sol, Ben Racicot, Andrew Eckart, Chad, angular-bull, José G. Ramírez, Brian Frichette, Jean-Sébastien Goupil and 47 more
If this is really the best practice to handle this in Angular, basically we can dump Angular and start using JQuery again.
Sorry, but I cannot take this approach seriously.
Reacted by kiwileaks78, Kou Lor, Renato Araujo Carneiro, Adrian Gallus, 0x70b1a5, Jared Grabill, Tomasz Kula, AlexQQ, Dinesh Balaji, Peter Müller and 80 more
Reacted by clay, Alexey Zuev, Trotyl Yu, Maxence Frenette, Michael Thomas, David Herges, Leon, Michael Prentice, N8 May, Kevin Buhmann and 1 more
I don't know what is the best approach for modals and lazy loading, but anyway in your plunkr it works as intended and there's no angular bug here.
For discussion about modals it's better to use support channels such as gitter and stackoverflow
Reacted by Yaser Aslam Kazi, Boris Joskic, Mitja O., Jacob Weisenburger, Murhaf Sousli, Alex Voerman, redevill, G Novotny, Fernando Leal, Denis and 9 more
Faced with same issue today - component for modal defined in lazy loaded module does not handled by app modal service (it cant find it despite entryComponents usage). Forced to move it to main bundle that breaks lazy modules structure and usage logic 😕
Reacted by Renato Araujo Carneiro, Eduard Kolosovskyi, 0x70b1a5, Reza Kajbaf, AlexQQ, oky1, yushaoyi, Alex Burykin, Andrew Eckart, EmmanuelAtx and 37 more
Same issue there ... Impossible to use an entryComponents in a lazyLoaded module without breaking the lazy modules logic : My LazyLoaded module depends on an entryComponent declared in my AppModule :(
Reacted by Renato Araujo Carneiro, Juan Pinilla, Eduard Kolosovskyi, Alex Burykin, Chad, Lucas Basquerotto, Saif Adnan, André Furchner, Alex, Bastien Collet and 16 more
Ran into this same issue, I did come to a resolution though after looking through ng-bootstrap modal source code. Essentially the issue (from my understanding please correct me if I'm wrong) is that your lazy modules are not included in the initial injector when your app is bootstrapped and the initial injector cannot be modified once it is set. Which is why you get the error in the OP. When one of your lazy modules is loaded it gets its own injector with references to whatever you declare or provide in your lazy module. That being said, as long as wherever you create your dynamic component at has reference to the correct injector and componentFactoryResolver you can indeed reference an entryComponent outside of the lazy loaded module.
So, what I did was create a singleton service to store the injector and componentFactoryResolver of the dynamic component I want to create. This service needs to be outside of your lazy modules. Then whenever I go to dynamically create a component I can call this service to get what I need.
Code below will be wherever you are creating your dynamic component outside of your lazy module
Thanks @jmcclanahan. Are you able to tell us a bit more about what that service looks like and how it is set up? I am struggling with a similar issue, trying ti dynamically load a component from one lazy loaded module into a component from another lazy-loaded module and even though (I think) I have added the entry components in the right place, I get told I haven't. Thanks
@gcorgnet - Sure. So my use case was, I have a common toolbar on every page of my app, but within the toolbar I wanted to add buttons and tools for additional functionality based on what page I was on. In order to achieve this I created a component to hold these buttons/tools and their logic and I wanted to keep these components inside of the module they are associated with. So, essentially nothing outside of my module knows anything about these specific toolbar functions while still being able to show them on the common toolbar. Below is my working solution which I hope will help you:
All I'm doing with the toolbar service is creating an observable, so your lazy component can pass it's componentFactoryResolver and injector references to the toolbar common component that is listening for the receiveContext Event.
Finally, in my core toolbar component I'm subscribing to the observable in the toolbar service so that anytime an event is fired it will try to inject and create the lazy component that I need. It's also important to destroy the componentRef that you create otherwise you will end up with memory leaks.
Your dynamically created component will then be placed wherever you put the corresponding #toolbarTarget
<div#toolbarTarget></div>
As an aside, I'm using this line this.component = data['toolbar']; in the common toolbar component to get the lazy loaded component reference from the route. If you want to see the full code for this I can post that, but it's outside the scope of this discussion.
@jmcclanahan I have one question (probably a stupid one) about your code, when you mention the "lazy loaded toolbar component", you put the service call code in the ngOnInit section, but how do you manage to initialize it (the component) without having to add it to the HTML code and hide it later in the DOM? Since that component is only going to be used in the topbar, I don't load it anywhere else before.
Even if it did work, it does not address the REAL problem. the problem is if I add a component to the entryComponents of an NgModule, and that module is imported into a lazily loaded module in my app, then the entryComponents should automatically get registered with the apps entry components. There should be no further work required. I would further say that any component in the entryComponents of any NgModule should automatically wind up in the entryComponents of the app module. This is currently not the case. If it were then we wouldn't be here.
Reacted by denkerny, Dzhuneyt, Yuriy Grunin, cedvdb and Ryan Smith
@mlc-mlapis Do you mean if different components have the same name? First match or an error if there are multiple matches. I could live with a requirement that entry component names must be unique.
@mlc-mlapis I think that the limitation in angular that many would like to overcome is that entry components can only be instantiated and placed in the dom by the app. The app does not need to know about a dialog box that a module that I import into a lazy loaded module uses. The current concept of entry components breaks the modular pattern of angular.
AFAIK with Ivy entryComponents will no longer be necessary, right? Since components will have locality so just dynamically importing them will always work?
I believe that this is fixed in Angular v9 running ivy. If anyone still faces similar problems, please open a new issue with a minimal reproduce scenario. Thnx!
Activity
DzmitryShylovich commentedon Feb 6, 2017
After some investigation I figured out that in your in works as designed.
Entry components follow the same rules as providers in lazy loaded modules. https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-lazy-loaded-module-provider-visibility
They are only visible inside lazy loaded module.
marvinscharle commentedon Feb 6, 2017
Ok, if this is the intended behavior, how can specific tasks like the use cases above be accomplished?
I don't think that blowing up the main module is a proper solution.
DzmitryShylovich commentedon Feb 6, 2017
If you want to use an entry component in
MainComponentthen you should provide it inAppModulemarvinscharle commentedon Feb 6, 2017
So your solution indeed is to move everything into the main module?
IMO this totally breaks the module concept of Angular which should allow to group functionality blocks into one module.
This means basically that things like modals are impossible to use with a lazy load architecture.
DzmitryShylovich commentedon Feb 6, 2017
You need to use an api similar to material's https://material.angular.io/components/component/dialog
Then it should work
marvinscharle commentedon Feb 6, 2017
Wow, so your opinion is that we should rather move DOM content, like Angular Material does. See here: https://github.com/angular/material2/blob/master/src/lib/core/portal/dom-portal-host.ts#L30-L55
If this is really the best practice to handle this in Angular, basically we can dump Angular and start using JQuery again.
Sorry, but I cannot take this approach seriously.
DzmitryShylovich commentedon Feb 6, 2017
I don't know what is the best approach for modals and lazy loading, but anyway in your plunkr it works as intended and there's no angular bug here.
For discussion about modals it's better to use support channels such as gitter and stackoverflow
marvinscharle commentedon Feb 6, 2017
Ok, in this case I'm changing this issue into a feature request.
IMO these use cases should be handled by Angular itself rather than by moving things along the DOM manually.
artaommahe commentedon Mar 6, 2017
Faced with same issue today - component for modal defined in lazy loaded module does not handled by app modal service (it cant find it despite
entryComponentsusage). Forced to move it to main bundle that breaks lazy modules structure and usage logic 😕kiwileaks78 commentedon Mar 7, 2017
Same issue there ... Impossible to use an entryComponents in a lazyLoaded module without breaking the lazy modules logic : My LazyLoaded module depends on an entryComponent declared in my AppModule :(
jmcclanahan commentedon Mar 13, 2017
Ran into this same issue, I did come to a resolution though after looking through ng-bootstrap modal source code. Essentially the issue (from my understanding please correct me if I'm wrong) is that your lazy modules are not included in the initial injector when your app is bootstrapped and the initial injector cannot be modified once it is set. Which is why you get the error in the OP. When one of your lazy modules is loaded it gets its own injector with references to whatever you declare or provide in your lazy module. That being said, as long as wherever you create your dynamic component at has reference to the correct
injectorandcomponentFactoryResolveryou can indeed reference anentryComponentoutside of the lazy loaded module.So, what I did was create a singleton service to store the
injectorandcomponentFactoryResolverof the dynamic component I want to create. This service needs to be outside of your lazy modules. Then whenever I go to dynamically create a component I can call this service to get what I need.Code below will be wherever you are creating your dynamic component outside of your lazy module
Then in your parent component for your entryComponent you'll inject
injectorandcomponentFactoryResolverand set their values in the shared service:Let me know if this doesn't make any sense and I can elaborate further. Edit I forgot to mention I'm using Angular 2.4.9.
gcorgnet commentedon Mar 20, 2017
Thanks @jmcclanahan. Are you able to tell us a bit more about what that service looks like and how it is set up? I am struggling with a similar issue, trying ti dynamically load a component from one lazy loaded module into a component from another lazy-loaded module and even though (I think) I have added the entry components in the right place, I get told I haven't. Thanks
jmcclanahan commentedon Mar 20, 2017
@gcorgnet - Sure. So my use case was, I have a common toolbar on every page of my app, but within the toolbar I wanted to add buttons and tools for additional functionality based on what page I was on. In order to achieve this I created a component to hold these buttons/tools and their logic and I wanted to keep these components inside of the module they are associated with. So, essentially nothing outside of my module knows anything about these specific toolbar functions while still being able to show them on the common toolbar. Below is my working solution which I hope will help you:
All I'm doing with the toolbar service is creating an observable, so your lazy component can pass it's
componentFactoryResolverandinjectorreferences to the toolbar common component that is listening for thereceiveContextEvent.toolbar.service.ts
In your lazy loaded component you'll want to inject
componentFactoryResolverandinjectorand fire an event in the toolbar service.lazy loaded toolbar component
Finally, in my core toolbar component I'm subscribing to the observable in the toolbar service so that anytime an event is fired it will try to inject and create the lazy component that I need. It's also important to destroy the
componentRefthat you create otherwise you will end up with memory leaks.Your dynamically created component will then be placed wherever you put the corresponding
#toolbarTargetAs an aside, I'm using this line
this.component = data['toolbar'];in the common toolbar component to get the lazy loaded component reference from the route. If you want to see the full code for this I can post that, but it's outside the scope of this discussion.Let me know if you have any further questions!
Dunos commentedon May 4, 2017
@jmcclanahan I have one question (probably a stupid one) about your code, when you mention the "lazy loaded toolbar component", you put the service call code in the ngOnInit section, but how do you manage to initialize it (the component) without having to add it to the HTML code and hide it later in the DOM? Since that component is only going to be used in the topbar, I don't load it anywhere else before.
99 remaining items
denkerny commentedon Nov 27, 2019
looks like it doesnt work with --prod mode
broweratcognitecdotcom commentedon Nov 27, 2019
Even if it did work, it does not address the REAL problem. the problem is if I add a component to the entryComponents of an NgModule, and that module is imported into a lazily loaded module in my app, then the entryComponents should automatically get registered with the apps entry components. There should be no further work required. I would further say that any component in the entryComponents of any NgModule should automatically wind up in the entryComponents of the app module. This is currently not the case. If it were then we wouldn't be here.
mlc-mlapis commentedon Nov 27, 2019
@broweratcognitecdotcom What about conflicts and priorities from different injection levels?
Dzhuneyt commentedon Nov 27, 2019
@mlc-mlapis order should be on a first-come, first-served basis in my opinion.
broweratcognitecdotcom commentedon Nov 28, 2019
@mlc-mlapis Do you mean if different components have the same name? First match or an error if there are multiple matches. I could live with a requirement that entry component names must be unique.
mlc-mlapis commentedon Nov 28, 2019
@broweratcognitecdotcom Hi, yes. Because all variants have to be covered if we are talking about general principles.
broweratcognitecdotcom commentedon Nov 28, 2019
@mlc-mlapis I think that the limitation in angular that many would like to overcome is that entry components can only be instantiated and placed in the dom by the app. The app does not need to know about a dialog box that a module that I import into a lazy loaded module uses. The current concept of entry components breaks the modular pattern of angular.
Airblader commentedon Nov 28, 2019
AFAIK with Ivy entryComponents will no longer be necessary, right? Since components will have locality so just dynamically importing them will always work?
mlc-mlapis commentedon Nov 28, 2019
@Airblader You are right, we know. It was just a small reminiscence back to history. 😄
th0r commentedon Nov 28, 2019
For those who still struggling with this issues here is the solution that actually works: #14324 (comment)
Jimmysh commentedon Dec 4, 2019
I publish a package for this issues. some code copy from jonrimmer.
It looks like that the issue does not exist in Angular 9. If you need fix this now. you can use
@aiao/lazy-componentsample code here
https://github.com/aiao-io/aiao/tree/master/integration/lazy-component
pkozlowski-opensource commentedon Mar 11, 2020
I believe that this is fixed in Angular v9 running ivy. If anyone still faces similar problems, please open a new issue with a minimal reproduce scenario. Thnx!
angular-automatic-lock-bot commentedon Apr 11, 2020
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.