In this article we’ll continue reviewing Angular module best practices. We’ve already covered how the SharedModule helps us eliminating 3rd party module listings and circular dependencies. We also reviewed the benefits of having a separate RoutingModule. Now we’ll check how to make true singleton services and organize them using the CoreModule.
As usual, there’s no CoreModule class shipping with Angular, we’re talking about best practices and conventions here.
What’s in a CoreModule?
The idea behind a CoreModule is that we put all global services in one place and import them to where they are required. The following services would be global:
- Your HttpClient wrapper service: it’s a great idea to create a wrapper around it. You’d shield yourself from obsoletions and breaking changes. Consider that HttpClient is the second Angular Http service implementation. No guarantee there wouldn’t be a third.
- Your Authentication service, if there is one. No point having multiple instances for multiple modules.
- Your logging service.
- A wrapper around the ngRx store – we’ll cover this much later.
Note that contrary to SharedModule, CoreModule does contain logic. You don’t need to keep it stupid, actual business rules can and should be written here.
Global singletons in Angular
Angular services are application-wide singletons, right? Well, the correct answer is that it depends. As long as you don’t use lazy-loading then technically they are. As soon as you start to lazy-load your modules it gets trickier.
If you have a theoretical BuzzService and inject it into your eagerly loaded AppComponent then you have one instance. It’ll be a singleton the same instance in all your other eagerly loaded components as well.
Let’s assume you have a lazy-loaded Settings and Dashboard module, both having components injecting BuzzService. When you load your SettingsModule you’ll get a second instance of BuzzService. The same happens when you load the DashboardModule – boom, a third instance. Depending on your use case this might or might not cause problems. Let’s see how to solve this and make our CoreModule services true singletons.
forRoot and providing services
Let’s see the code for a complete CoreModule and dissect it:
Until line 8 there’s nothing really new or noteworthy. On line 9, however we are defining a static method. Its name is forRoot, a convention you should follow too. This forRoot method returns an implementor of the ModuleWithProviders interface. This interface has two members, the type of the module called ngModule, and an array of providers.
As you can see in the example we use the same type as defined in the module. We list the providers here instead of the NgModule decorator. The last thing we need to do is import our CoreModule using the forRoot method in our AppModule. Note that you only use forRoot in AppModule and nowhere else. Here’s an example:
Notice that on line 10 we used CoreModule.forRoot instead of the “normal” way. This ensures that all our services provided by CoreModule (at least those which are listed in the forRoot method) are true global singletons, regardless of how they are loaded.