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

AngularRouter

Angular Problem Overview


What is the differences between these two and what are the use cases for each?

The docs aren't exactly helpful:

> forRoot creates a module that contains all the directives, the given > routes, and the router service itself. > >forChild creates a module that > contains all the directives and the given routes, but does not include > the router service.

My vague guess is that one is for the 'main' module and the other is for any imported modules (since they would already have the service available from the main module), but I can't really think of a use case.

Angular Solutions


Solution 1 - Angular

I strongly suggest reading this article:

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}]
    };
}

Solution 2 - Angular

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.

Solution 3 - Angular

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.

Solution 4 - Angular

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

Solution 5 - Angular

If appRoutes contains path to various functions in the site (admin crud, user crud, book crud) and we want to separate them we could simply do that :

 imports: [
    BrowserModule, HttpModule,
    AppRoutingModule,
    RouterModule.forRoot(categoriesRoutes),
    RouterModule.forRoot(auteursRoutes),
  ],

And for routes :

const auteursRoutes:Routes=[
  {path:'auteurs/ajouter',component:CreerAuteurComponent},
]
const categoriesRoutes: Routes = [
   
   
  {path:'categories/consulter',component:ConsultercategoriesComponent},
  {path:'categories/getsouscategoriesbyid/:id',component:GetsouscategoriesbyIDComponent},
  {path:'categories/ajout',component:CreerCategorieComponent},
 {path:'categories/:id',component:ModifiercategorieComponent},
 {path:'souscategories/ajout/:id',component:AjoutersouscategorieComponent},
 {path:'souscategories/lecture/:id1',component:SouscategoriesComponent},
 {path:'souscategories/modifier/:id1',component:ModifiersupprimersouscategorieComponent},
  {path:'uploadfile',component:UploadfileComponent},
  {path:'categories',component:ConsultercategoriesComponent},

   
  
]
 

Solution 6 - Angular

The forRoot Static Method:

RouterModule.forRoot(routes)

The forRoot static method is the method that configures the root routing module for your app. When you call RouterModule.forRoot(routes), you are asking Angular to instantiate an instance of the Router class globally. Just like Angular creates a new base AppModule to import all of your feature modules, it also provides the AppRoutingModule to import all of your child routes.

In the new app that you have created via the Angular CLI, the forRoot method is actually already being used inside of the app-routing.module.ts. In your app, you only want to use the forRoot method once. This is because this method tells Angular to instantiate an instance of the Router class under the hood, and there can be only one router in your app. The forRoot static method is a part of a pattern that ensures that you are using singleton classes.

Child Routes:

RouterModule.forChild(routes)

When you are using the forChild static method, you are basically telling Angular, "There is already a Router instance available in the app so please just register all of these routes with that instance." The forChild method is the method that you will call to register routes throughout your app and you will use it inside of the child, routing modules that you create.

The forChild static method is useful because it plays a core part of Angular module functionality by allowing you to maintain separation of concerns within your app.

Suppose you want to create a new feature module for user settings in your app and this feature will contain a few routes. Instead of adding these routes into the AppRoutingModule directly, which would eventually become untenable as your app grows, you can maintain separation of concerns within your app by using the forChild method. First, create a new UserSettingsRoutingModule.

import { NgModule } from  '@angular/core';
import { Routes, RouterModule } from  '@angular/router';

import { UserSettingsComponent } from './user-settings.component';
import { UserProfileComponent } from  './user-profile/user-profile.component';

const  routes:  Routes  = [
    {
        path:  'settings',
        component:  UserSettingsComponent,
        children: [
            {
                path:  'profile',
                component: UserProfileComponent 
            }
        ]
    }
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export  class  UserSettingsRoutingModule { }

Note the use of the forChild method above. Since you have already used the forRoot method, you'll just want to register your routes to the already instantiated app router.

Now you need to create your UserSettingsModule like this:

import { NgModule, CommonModule } from  '@angular/core';
import { UserSettingsRoutingModule } from  './user-settings-routing.module';

@NgModule({
    imports: [
        CommonModule,
        UserSettingsRoutingModule
    ],
    // Configure the rest of your module here
})
export class UserSettingsModule { }

And there you have it! Now all you would need to do is import this UserSettingsModule into your root, AppModule, and your child routes that point to their respective components would be configured within your app.

I hope that this guide has helped you to understand how the Angular Router static methods forRoot and forChild can help you to create well-structured routes within your app. For more information, check out documentation

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVSOView Question on Stackoverflow
Solution 1 - AngularMax KoretskyiView Answer on Stackoverflow
Solution 2 - AngularRoyi NamirView Answer on Stackoverflow
Solution 3 - AngularMarcinView Answer on Stackoverflow
Solution 4 - AngularHeshanView Answer on Stackoverflow
Solution 5 - AngularHmaied BelkhiriaView Answer on Stackoverflow
Solution 6 - AngularBhavesh AjaniView Answer on Stackoverflow