RouterModule.forRoot (ROUTES) 대 RouterModule.forChild (ROUTES)
이 둘의 차이점은 무엇이며 각각의 사용 사례는 무엇입니까?
문서는 정확하게 도움이되지 않습니다 :
forRoot는 모든 지시문, 지정된 경로 및 라우터 서비스 자체를 포함하는 모듈을 만듭니다.
forChild는 모든 지시문과 지정된 경로를 포함하지만 라우터 서비스는 포함하지 않는 모듈을 만듭니다.
내 모호한 추측은 하나는 '메인'모듈 용이고 다른 하나는 가져온 모듈 용이라는 것입니다 (메인 모듈에서 이미 서비스를 사용할 수 있기 때문에).하지만 실제로 사용 사례를 생각할 수 없습니다.
이 기사를 읽는 것이 좋습니다.
공급자가있는 모듈
모듈을 가져올 때 일반적으로 모듈 클래스에 대한 참조를 사용합니다.
@NgModule({
providers: [AService]
})
export class A {}
-----------------------------------
@NgModule({
imports: [A]
})
export class B
이렇게하면 모듈에 등록 된 모든 공급자 A
가 루트 인젝터에 추가되고 전체 애플리케이션에서 사용할 수 있습니다.
그러나 다음과 같은 공급자에 모듈을 등록하는 또 다른 방법이 있습니다.
@NgModule({
providers: [AService]
})
class A {}
export const moduleWithProviders = {
ngModule: A,
providers: [AService]
};
----------------------
@NgModule({
imports: [moduleWithProviders]
})
export class B
이것은 이전 것과 동일한 의미를 갖습니다.
지연로드 된 모듈에는 자체 인젝터가 있다는 것을 알고있을 것입니다. 따라서 AService
전체 응용 프로그램에서 사용할 수 있도록 등록 하고 일부 BService
는 지연로드 된 모듈에서만 사용할 수 있도록 등록하려고한다고 가정합니다 . 다음과 같이 모듈을 리팩터링 할 수 있습니다.
@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
이제는 BService
하위 지연로드 모듈에서만 사용할 수 AService
있으며 전체 애플리케이션에서 사용할 수 있습니다.
위를 다음과 같이 내 보낸 모듈로 다시 작성할 수 있습니다.
@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
RouterModule과 어떤 관련이 있습니까?
둘 다 동일한 토큰을 사용하여 액세스한다고 가정합니다.
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}]
};
}
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.
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 ? becuase 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 prodivers ( sorry :))
How ? Like this :
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 becuase 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
andforChild
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 becuase 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.
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},
]
'development' 카테고리의 다른 글
Matplotlib는 산점도 점을 선으로 연결-Python (0) | 2020.09.21 |
---|---|
새 행에 대해서만 기본 NOW ()로 타임 스탬프 열 추가 (0) | 2020.09.21 |
Visual Studio에서 C ++ 17 컴파일을 활성화하는 방법은 무엇입니까? (0) | 2020.09.21 |
LINQ to XML에서 네임 스페이스 무시 (0) | 2020.09.21 |
사용자 인터페이스의 "ID"또는 "ID" (0) | 2020.09.21 |