Skip to content

Make Material modules compatible with lazy-loaded components #1071

@RoxKilly

Description

@RoxKilly

Bug, 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.

Metadata

Metadata

Assignees

Labels

P1Impacts a large percentage of users; if a workaround exists it is partial or overly painful

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions