Make Material modules compatible with lazy-loaded components
See original GitHub issueBug, feature request, or proposal:
Proposal
What is the expected behavior?
Services that are only practically useful as singletons should be available as such. MdIconRegistry
is a perfect example. SinceMdIconRegistry.addSvgIconSet()
adds icons that should be available app-wide, this service is most useful as a singleton. Instead of simply offering MdIconModule
as an export, offer MdIconModule.forRoot
as an alternative that can be used to add providers only to the root module in accordance with the Angular docs. So my proposal is to remove services that are meant to be used as a singleton by client modules from the providers
arrays of material modules. Change
@NgModule({
providers: [ MdIconRegistry]
})
export class MdIconModule{ }
to
@NgModule({
providers: [] //empty since we want a singleton service
})
export class MdIconModule{
static forRoot() {
return {ngModule: MdIconModule, providers: [ MdIconRegistry]};
}
}
So that in the root AppModule
we can import MdIconModule.forRoot()
, thus registering its provider just once to all components including lazy-loaded ones.
What is the current behavior?
Because even services that should be singletons are included in the providers
array of their respective Material modules, I don’t know how it’s possible to make them singletons. To pick up the example above, when I execute MdIconRegistry.addSvgIconSet()
in my AppComponent
, the icons are not visible to lazy-loaded modules because they have a different Angular services injector.
What are the steps to reproduce?
Open this plunker and navigate to the LazyLoadedComponent
view. You’ll notice that the icon doesn’t show up and the console logs the error below:
Error retrieving icon: Error: Unable to find icon with the name “:play”
What is the use-case or motivation for changing an existing behavior?
The current setup makes services that are only practical as singletons really difficult to use. The only workarounds I’ve found are:
- Load the whole app at bootstrap (no lazy-loading): this obviously has very negative performance repercussions
- Register the same icon set in every component: this triggers a new http request for the same file every time the user navigates, not only harming client performance but also taxing the server
Which versions of Angular, Material, OS, browsers are affected?
Angular 2 rc.5
with Material 2 alpha 7-3
Is there anything else we should know?
I’ve been studying this issue for a while now and this post is the culmination of my observations in #1022 and #1016 . The Angular docs recommend a strategy akin to what I propose here:
forRoot and forChild are conventional names for methods that deliver different import values to root and feature modules. Follow the convention when you write similar modules for your application.
Issue Analytics
- State:
- Created 7 years ago
- Reactions:3
- Comments:5 (1 by maintainers)
Top GitHub Comments
@RoxKilly Does this still require a workaround? @jelbourn mentioned it should be fixed, Could someone please provide a working example if there is a proper way to do this?
A user with the handle ‘James’ on StackOverflow helped me find an acceptable workaround until lazy routes are supported.
Basically, instead of importing
MdIconModule
, build a custom module (I call itMdIconFixedModule
) that declares and exportsMdIcon
, but that has an emptyproviders
array:Import
MdIconFixedModule.forRoot()
intoAppModule
. This provides bothMdIcon
and a singletonMdIconRegistry
. To use icons in other modules, importMdIconFixedModule
. This makesMdIcon
available without a separate instance ofMdIconRegistry
, thus fixing the problem.See this workaround on plunker