Shared Modules In Angular Apps: Providers Best Practices And What Does `forRoot()` Do?

When you write a shared module for your application, that contains stuff you use in other modules in the same app, you typically want all the services in that module shared only once.

If you just put the services in the providers property of the shared module’s NgModule() decorators, you might get weird cases, especially with lazy loading, when it feels like there’s more than one instance of the service. This is bad if you want to have shared state.

So, there is a better way to write your shared modules:

import {NgModule, ModuleWithProviders} from '@angular/core';
import {CommonModule} from '@angular/common';
// ... other imports

export const providers = [
    // ... your shared services here
];

@NgModule({
    declarations: [...],
    imports: [
        CommonModule, 
        SomeLibraryModule,
        ...],
    exports: [
        SomeLibraryModule.forRoot()
        ...
    ]
})
export class SharedModule {
    static forRoot() : ModuleWithProviders {
        return {
            ngModule: SharedModule,
            providers: [...providers]
        };
    }
}

The forRoot() pattern / convention is very common in libraries, and you’ll see it in things like ng-bootstrap and others. The name isn’t special for the compiler / framework, but it’s a common pattern.

When using these, in the imports section you can just import the module itself (which gives you any declarations needed like directives etc), and in the exports use the forRoot() version of the module so that it’s available for consumers of your shared module.

Then in your other application modules you can add SharedModule to their NgModule‘s imports normally, except for the AppModule.

The AppModule will be the only place where you add SharedModule.forRoot(), like:

Then in your AppModule, import it as:

@NgModule({
    declarations: [...],
    imports: [BrowserModule, SharedModule.forRoot(), ...],
    ...
})
export class AppModule {

}

There is one exception to this though. Your tests.

If you are writing any unit tests where you are importing the SharedModule, you will probably need to import the module with its providers, because there is no AppModule in the test.

Something like:

TestBed.configureTestingModule({
    imports: [ SharedModule.forRoot(), ... ]
})
...

If you haven’t already, have a look at the NgModule official documentation. There’s a main guide, and an FAQ page.

And of course let me know if you have any questions / problems.

Share With Friends:

How did I learn that?

As a bonus for coming here, I'm giving away a free newsletter for web developers that you can sign up for from here.

It's not an anything-and-everything link list. It's thoughtfully collected picks of articles and tools, that focus on Angular 2+, ASP.NET (4.x/MVC5 and Core), and other fullstack developer goodies.

Take it for a test ride, and you may unsubscribe any time.

You might also want to support me by checking these out [Thanks]:

  • El Océano

    thank you for the article but I still cant see the benefict. Could you explain what is the difference with no using forRoot ()? Anyway thanks

    • It’s for when you need to make sure the services are singletons, meaning there’s just one shared instance of each service regardless of how many modules the shared module has been imported into.

      You can do this by excluding the providers of these services from the shared module itself (so whenever it’s imported, no providers are included), and only include the providers once in the `.forRoot()` function, which you only call once, in the `AppModule`.

      This guarantees that the providers are only called once, which means single instances of the services.

      • El Océano

        Thank you very much. Great explanation. I was having problems using services in feature modules and this helped a lot. Thanks, again @Meligy.

  • Pingback: pax 3 dhgate()

  • Pingback: what is cpmfun()

  • jhou

    why have to use forRoot() in the code:
    exports: [
    SomeLibraryModule.forRoot()

    ]

    Does this make SomeLibraryModule’s provider included multiple times, if the SharedModule is imported in both root and non-root modules?

    • Jonathan Bruno

      I did not get it too

      • jhou

        There is an example showing how to use forRoot pattern from material 2, although material2 deprecated it in recent implementation:

        import {MdButtonModule} from ‘./button/index’;
        import {MdDialogModule} from ‘./dialog/index’;
        import {MdAutocompleteModule} from ‘./autocomplete/index’;

        const MATERIAL_MODULES = [
        MdAutocompleteModule,
        MdButtonModule,
        MdDialogModule,

        ];

        /** @deprecated */
        @NgModule({
        imports: [
        MdAutocompleteModule.forRoot(),
        MdButtonModule.forRoot(),
        MdDialogModule.forRoot(),

        ],
        exports: MATERIAL_MODULES,
        })
        export class MaterialRootModule { }

        /** @deprecated */
        @NgModule({
        imports: MATERIAL_MODULES,
        exports: MATERIAL_MODULES,
        })
        export class MaterialModule {
        /** @deprecated */
        static forRoot(): ModuleWithProviders {
        return {ngModule: MaterialRootModule};
        }
        }

        Even better, a new solution to replace forRoot pattern, as discusses in:

        why was forRoot() removed from Material2 and what can I do about in regards to lazy loaded feature modules?
        https://stackoverflow.com/questions/44532307/why-was-forroot-removed-from-material2-and-what-can-i-do-about-in-regards-to-l

        An official proposal has been submitted on the Angular repository: https://github.com/angular/angular/issues/13854

        Here is a link to one example provider factory of Angular Material:
        https://github.com/angular/material2/blob/master/src/cdk/a11y/live-announcer.ts

  • Jonathan Bruno

    What if I need a Service that lives on a LazyLoad SubModule?

    https://stackoverflow.com/questions/45634391/angular-provide-global-service-for-lazyloading-modules

    I think I cant use forRoot(), I cant even import the SubModule.
    Should I provide this Service directly on AppModule? Is this a good practice?