Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular, Lazy Loading, Inter-Module Dependencies, best practice

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.


Ideas:

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?!


Some code snippets of the current (non-working) approach:

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) { ... }
}
like image 729
Benjamin M Avatar asked Jul 22 '26 15:07

Benjamin M


2 Answers

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.

like image 83
pixelbits Avatar answered Jul 24 '26 11:07

pixelbits


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.

like image 29
Hasnain Bukhari Avatar answered Jul 24 '26 10:07

Hasnain Bukhari



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!