RouterModule.forRoot(ROUTES) vs RouterModule.forChild(ROUTES)

I strongly suggest reading this article:

  • Avoiding common confusions with modules in Angular

Module with providers

When you import a module you usually use a reference to the module class:

@NgModule({
    providers: [AService]
})
export class A {}

-----------------------------------

@NgModule({
    imports: [A]
})
export class B

In this way all providers registered on module A will be added to the root injector and available for the entire application.

But there is another way to register a module with providers like this:

@NgModule({
    providers: [AService]
})
class A {}

export const moduleWithProviders = {
    ngModule: A,
    providers: [AService]
};

----------------------

@NgModule({
    imports: [moduleWithProviders]
})
export class B

This has the same implications as the previous one.

You probably know that lazy loaded modules have their own injector. So suppose you want to register AService to be available for the entire application, but some BService to be available to only lazy loaded modules. You can refactor your module like this:

@NgModule({
    providers: [AService]
})
class A {}

export const moduleWithProvidersForRoot = {
    ngModule: A,
    providers: [AService]
};

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [BService]
};

------------------------------------------

@NgModule({
    imports: [moduleWithProvidersForRoot]
})
export class B
    
// lazy loaded module    
@NgModule({
    imports: [moduleWithProvidersForChild]
})
export class C

Now BService will only be available for child lazy loaded modules and AService will be available for the entire application.

You can rewrite the above as an exported module like this:

@NgModule({
    providers: [AService]
})
class A {
    forRoot() {
        return {
            ngModule: A,
            providers: [AService]
        }
    }

    forChild() {
        return {
            ngModule: A,
            providers: [BService]
        }
    }
}

--------------------------------------

@NgModule({
    imports: [A.forRoot()]
})
export class B

// lazy loaded module
@NgModule({
    imports: [A.forChild()]
})
export class C

###How is that relevant to RouterModule? Suppose they are both accessed using the same token:

export const moduleWithProvidersForRoot = {
    ngModule: A,
    providers: [{provide: token, useClass: AService}]
};

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [{provide: token, useClass: BService}]
};

With separate configurations when you request token from a lazy loaded module you will get BService just as planned.

RouterModule uses ROUTES token to get all routes specific to a module. Since it wants routes specific to lazy loaded module to be available inside this module (analogues to our BService) it uses different configuration for the lazy loaded child modules:

static forChild(routes: Routes): ModuleWithProviders {
    return {
        ngModule: RouterModule, 
        providers: [{provide: ROUTES, multi: true, useValue: routes}]
    };
}

I think the answers are right but I think something is missing.
The thing which is missing is "why and what it solves ?".
Ok let's start.

First let's mention some info:

All modules has access to the root services.
So even lazy loaded modules can use a service which was provided in app.module.
What will happen if a lazy loaded module will provide to itself a service which the app module already provided ? there will be 2 instances.
It's not a problem but sometimes it is.
How can we solve it ? simply don't import a module with that provider to lazy loaded modules.

End of story.

This ^ was just to show that lazy loaded modules has their own injection point ( as opposed to non-lazy-loaded modules).

But what happens when a shared(!) module has declared providers, and that module is imported by lazy and app.module? Again, like we said, two instances.

So how can we solve this in the shared module POV ? We need a way not to use providers:[] ! Why? because they will be auto imported to both consuming lazy and app.module and we don't want that as we saw that each will have a different instance.

Well, it turns out that we can declare a shared module that won't have providers:[], but still, will provide providers ( sorry :))

How? Like this :

enter image description here

Notice, no providers.

But

  • what will happen now when app.module will import the shared module with POV of service ? NOTHING.

  • what will happen now when a lazy module will import the shared module with POV of service ? NOTHING.

Entering Manual mechanism via convention :

You will notice that the providers in the pictures have service1 and service2

This allows us to import service2 for lazy loaded modules and service1 for non-lazy modules. ( cough...router....cough)

BTW, no one is stopping you to call forRoot within a lazy module. but you will have 2 instances because app.module should also do it - so don't do it in lazy modules.

Also - if app.module calls forRoot (and no one calls forchild) - that's fine, but root injector will only have service1. ( available to all app)

So why do we need it? I'd say :

It allows a shared module , to be able to split its different-providers to be used with eager modules and lazy modules - via forRoot and forChild convention. I repeat : convention

That's it.

WAIT !! not a single word about singleton ?? so why do I read singleton everywhere ?

Well - it's hidden in the sentence above ^

It allows a shared module, to be able to split its different-providers to be used with eager modules and lazy modules - via forRoot and forChild.

The convention (!!!) allows it to be singleton - or to be more precise - if you won't follow the convention - you will NOT get a singleton.
So if you only load forRoot in the app.module , then you get only one instance because you only should call forRoot it in the app.module.
BTW - at this point you can forget about forChild. the lazy loaded module shouldn't / won't call forRoot - so you're safe in POV of singleton.

forRoot and forChild are not one unbreakable package - it's just that there is no point of calling for Root which obviously will be loaded only in app.module without giving the ability for lazy modules , have their own services , without creating new services-which-should-be-singleton.

This convention give you a nice ability called forChild - to consume "services only for lazy loaded modules".

Here is a demo Root providers yields positive numbers , lazy loaded modules yields negative numbers.


Documentation clearly states what is the purpose of this distinction here: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

Call forRoot only in the root application module, AppModule. Calling it in any other module, particularly in a lazy loaded module, is contrary to the intent and is likely to produce a runtime error.

Remember to import the result; don't add it to any other @NgModule list.

Every application has exactly one starting point (root) where the main routing service should be initialized with forRoot, while routes for particular "child" features should be registered additionaly with forChild. It is extremely useful for submodules and lazy loaded modules which do not have to be loaded at the application start, and as @Harry Ninh said they are told to reuse RouterService instead of registration of the new service, which may cause a runtime error.


Consider this as the implementation of router module for the sake of understanding. Lazyloaded routes and common routes has to be handle seperately,supporting lazy loading

   @NgModule({
      declarations: [
        
      ],
      exports: [],
    })
    export class RouteModule {
      static forRoot(): ModuleWithProviders<RouteModule> {
        return { ngModule: RouteModule, providers: [routerHistoryService,handleCommonRoutesService] };
      }
    
      static forChild(): ModuleWithProviders<RouteModule> {
        return {
          ngModule: RouteModule, providers: [handleChildRouterService]
        };
      }
    }
    
    

forroot -> will add routerModule with providers (services) , so all the services need for common angular routing (example routerHistoryService ) will be injected to root injector from the app module(root module)

forchild -> will add routerModule with providers(services) ,but as common services(ex routerHistoryService ) already added in to root injector ,from the lazy loaded modules,we may able to use them and no need to add again,if we add it will create two instances. but there could be services, specifically needed for handle child routes. so in that case when call the forchild we can ,provide them (exmple :handleChildRouterService)

if the routerModule if we do not implement forRoot and forChild Consider the below scenario

1)in the root injectors routerHistoryService previous route is "home/lazymodule"

2)but in lazy module with new history service ,previous route is null

so it does not have data to go back when back button clicks. Thats why routerModule has implemeted following this pattern to ensure router module got only single instance throughout the application