Reputation: 24567
I have following example application layout:
app.module
|
|-- dashboard.module (lazy loaded)
|-- dashboard-routing.module (imported by dashboard.module)
|-- dashboard.service (provided by dashboard.module)
|-- dashboard.component (depends on dashboard.service)
|
|-- notifications.module (lazy loaded)
|-- notifications-routing.module (imported by notifications.module)
|-- notifications.service (provided by notifications.module)
|-- notifications.component (depends on notifications.service)
|
|-- my-notifications-dashboard-widget.component <- this should be added
|
|-- ...
Every module (dashboard
, notifications
, ...
) is getting lazy loaded.
Now I'd like to add some MyNotificationsDashboardWidgetComponent
, which depends on the notifications.service
. This widget then should get injected within the dashboard.service
.
This works well, as long as the provided WidgetComponent is inside the dashboard.module
. But if I put the component within the lazy loaded notifications.module
, it of course doesn't work.
Now I'd like to know, how to solve this in a good, module oriented way.
My requirements:
There should only be one notifications.service
, which should be used by notifications.component
and my-notifications-dashboard-widget.component
.
The big modules (dashboard
, notifications
) should be lazy loaded.
The dashboard.module
shouldn't know anything about the notifications.module
and / or the my-notifications-dashboard-widget.component
.
I guess, that I need some eager loaded module, which contains my-notifications-dashboard-widget.component
. So that this component and its provider is known before the dashboard.module
is getting loaded.
But then I'd have to put the notifications.service
in this module, too, I guess?!
dashboard.service:
@Injectable()
export class DashboardService {
constructor(@Inject(DASHBOARD_WIDGET) widgets) {
// inject all provided widgets
widgets.forEach(w => console.log(w.type));
}
}
notifications.module:
@NgModule({
imports: [ NotificationsRoutingModule ],
declarations: [ NotificationsComponent ],
providers: [
NotificationsService,
// this is currently not working, because of lazy loading
{
provide: DASHBOARD_WIDGET,
useValue: {
type: 'my-notifications'
component: MyNotificationsDashboardWidgetComponent
},
multi: true
}
],
entryComponents: [ MyNotificationsDashboardWidgetComponent ]
})
export class NotificationsModule { }
notifications.component:
@Component({ ... })
export class NotificationsComponent {
constructor(service: NotificationsService) { ... }
}
my-notifications-dashboard-widget.component:
@Component({ ... })
export class MyNotificationsDashboardWidgetComponent {
constructor(service: NotificationsService) { ... }
}
Upvotes: 3
Views: 1223
Reputation: 24567
Thanks for your inspiration. I've now created a working solution. It still looks a bit strange to me, and some things are a mystery to me, but at least it works.
Would be nice, if you could leave a comment with some additional information
app.module:
@NgModule({
imports: [
AppRoutingModule,
NotificationModule.forRoot()
]
})
export class AppModule { }
app-routing.module:
@NgModule({
imports: [
RouterModule.forRoot([
{
path: '/dashboard',
loadChildren: 'app/dashboard/dashboard.module#DashboardModule'
},
{
path: '/notifications',
loadChildren: 'app/notification/notification.module#NotificationModule'
}
])
]
})
export class AppRoutingModule { }
dashboard.module:
@NgModule({
providers: [ DashboardService ]
})
export class DashboardModule {
provideWidget(type: string, component: Type<any>) {
return [
{
provide: ANALYZE_FOR_ENTRY_COMPONENTS,
useValue: component,
multi: true
},
{
provide: DASHBOARD_WIDGET,
useValue: {
type: type,
component: component
},
multi: true
}
];
}
}
notification.module:
@NgModule({
imports: [
NotificationRoutingModule
],
declarations: [
NotificationComponent
]
})
export class NotificationModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: NotificationRootModule,
providers: [ ]
}
}
}
notification-root.module:
@NgModule({
declarations: [
TestWidgetComponent
],
providers: [
NotificationService,
DashboardModule.provideWidget('TEST', TestWidgetComponent)
]
})
export class NotificationRootModule { }
I guess I haven't understood forRoot
and ModuleWithProviders
completely. I don't understand why I have to create a separate NotificationRootModule
for this to work.
Now the TestWidgetComponent
and the NotificationService
are compiled into the app module. Though everything works as expected.
But: If I instead use ngModule: NotificationModule
within NotificationModule.forRoot()
, then it will put the whole module (including NotificationRoutingModule
and NotificationComponent
) inside the app module. (I can see that the browser doesn't lazy load anything when opening /notifications
URL.)
And now I don't know where the providers
belong. I can put them within @NgModule({ providers })
of the NotificationRootModule
or within the forRoot() { return { providers } }
. It seems to make no difference.
I hope you can enlighten me.
Thanks you!
Upvotes: -1
Reputation: 52867
Keep in mind that providers at the NgModule level are always registered with the root injector. There is only one root injector, and any provider registered with the root injector is available application-wide. The exception to this rule is lazy-loaded modules - lazy loaded modules create their own scoped injector. In other words, any providers registered with a module that is lazy-loaded will not be registered with the root injector.
In order to register providers with the root injector, regardless of whether or not they are lazy-loaded, you should follow the forRoot
convention. The idea is that the forRoot
method returns the module with providers, so that it can be imported by AppModule.
@NgModule({
providers: []
})
export class MyModule {
static forRoot(): ModuleWithProviders {
return {
NgModule: MyModule,
providers: [
MyProvider
]
}
}
}
Note: There is also the forChild
method you can implement that returns a module without providers. The intention for this method is so that you can re-use components from a module (including lazy-loaded modules) without actually registering any providers.
Upvotes: 4
Reputation: 468
In this case you are creating only one widget and in future, you might need more widgets to add.
Solution is, Make anohter shared module and create such component in that module. New shared module will be independent of other modules and all components inside it.
Import shared module in other modules and use it as you want.
Upvotes: 2