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.
I strongly suggest reading this article:
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
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}]
};
}