RouterModule.forRoot(ROUTES) vs RouterModule.forChild(ROUTES)
В чем разница между этими двумя и каковы варианты использования для каждого?
docs не совсем полезны:
forRoot создает модуль, содержащий все директивы, данные маршрутов и самого сервиса маршрутизатора.
forChild создает модуль, который содержит все директивы и данные маршруты, но не включает службы маршрутизатора.
Моя неопределенная догадка заключается в том, что для основного модуля есть модуль, а другой - для любых импортированных модулей (поскольку они уже будут иметь доступ к сервису из основного модуля), но я не могу думать о случае использования,
Ответы
Ответ 1
Я настоятельно рекомендую прочитать эту статью:
Модуль с провайдерами
При импорте модуля вы обычно используете ссылку на класс модуля:
@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}]
};
С отдельными конфигурациями, когда вы запрашиваете token
из лениво загруженного модуля, вы получаете BService
как и планировалось.
RouterModule использует маркер ROUTES
для получения всех маршрутов, характерных для модуля. Поскольку он хочет, чтобы маршруты, специфичные для лениво загруженного модуля, были доступны внутри этого модуля (аналог нашей BService), он использует другую конфигурацию для лениво загруженных дочерних модулей:
static forChild(routes: Routes): ModuleWithProviders {
return {
ngModule: RouterModule,
providers: [{provide: ROUTES, multi: true, useValue: routes}]
};
}
Ответ 2
В документах четко указывается, какова цель этого различия здесь: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root
Вызовите forRoot только в корневом прикладном модуле AppModule. Вызов его в любом другом модуле, особенно в ленивом загруженном модуле, противоречит намерению и может привести к ошибке выполнения.
Не забудьте импортировать результат; не добавляйте его в какой-либо другой список @NgModule.
Каждое приложение имеет ровно одну начальную точку (root), где главная служба маршрутизации должна быть инициализирована с помощью forRoot
, тогда как маршруты для определенных "дочерних" функций должны быть зарегистрированы дополнительно с помощью forChild
. Это чрезвычайно полезно для подмодулей и ленивых загружаемых модулей, которые не нужно загружать при запуске приложения, а так как @Harry Ninh сказал, что им предлагается повторно использовать RouterService вместо регистрации новой службы, что может привести к ошибке выполнения.
Ответ 3
Я думаю, что ответы правильные, но я думаю, что чего-то не хватает.
То, чего не хватает, это "почему и что это решает?".
Хорошо, давайте начнем.
Сначала позвольте упомянуть некоторую информацию:
Все модули имеют доступ к корневым сервисам.
Так что даже лениво загруженные модули могут использовать сервис, который был предоставлен в app.module
.
Что произойдет, если лениво загруженный модуль предоставит себе услугу, которую уже предоставил модуль приложения? будет 2 экземпляра.
Это не проблема, но иногда это так.
Как мы можем решить это? просто не импортируйте модуль с этим провайдером в лениво загруженные модули.
Конец истории.
Это было просто для того, чтобы показать, что у лениво загруженных модулей есть собственная точка впрыска (в отличие от не лениво загружаемых модулей).
Но что происходит, когда общий (!) Модуль объявляет providers
, и этот модуль импортируется lazy и app.module
? Опять же, как мы уже говорили, два случая.
Итак, как мы можем решить это в общем модуле POV? нам нужен способ не использовать providers:[]
! Зачем? потому что они будут автоматически импортированы как в lazy, так и в app.module, и мы не хотим этого, поскольку мы увидели, что у каждого будет свой экземпляр.
Что ж, получается, что мы можем объявить общий модуль, у которого не будет providers:[]
, но, тем не менее, он будет предоставлять продюсеров (извините :))
Как? Как это:
![enter image description here]()
Обратите внимание, нет поставщиков.
Но
-
что теперь будет, когда app.module будет импортировать общий модуль с POV сервиса? НИЧЕГО ТАКОГО.
-
что будет теперь, когда ленивый модуль будет импортировать общий модуль с POV службы? НИЧЕГО ТАКОГО.
Ввод ручного механизма через соглашение:
Вы заметите, что у поставщиков на фотографиях есть service1
и service2
Это позволяет нам импортировать service2
для лениво загруженных модулей и service1
для не ленивых модулей. (кашель... роутер.... кашель)
Кстати, никто не forRoot
вам вызывать forRoot
в ленивом модуле. но у вас будет 2 экземпляра, потому что app.module
также должен это делать - так что не делайте этого в ленивых модулях.
Также - если app.module
вызывает forRoot
(а никто не вызывает forchild
) - это нормально, но у корневого инжектора будет только service1
. (доступно для всех приложений)
Так зачем нам это? Я бы сказал:
Это позволяет совместно используемому модулю разделять своих разных провайдеров для использования с forRoot
модулями и ленивыми модулями - через forRoot
и forChild
. Я повторяю: конвенция
Это.
ПОДОЖДИТЕ! ни единого слова о синглтоне?? так почему я везде читаю синглтон?
Хорошо - это скрыто в предложении выше ^
Это позволяет совместно используемому модулю разделять своих разных провайдеров для использования с готовыми модулями и ленивыми модулями - через forRoot и forChild.
Соглашение (!!!) позволяет ему быть одноэлементным - или быть более точным - если вы не будете следовать соглашению - вы НЕ получите одноэлементный.
Поэтому, если вы загружаете только forRoot
в app.module
, то вы получаете только один экземпляр, потому что вам нужно только вызвать forRoot
в app.module
.
Кстати - на данный момент вы можете забыть о forChild
. ленивый загруженный модуль не должен/не будет вызывать forRoot
- так что вы в безопасности в POV синглтона.
forRoot и forChild не являются одним неразрушимым пакетом - просто нет смысла вызывать Root, который, очевидно, будет загружен только в app.module
не предоставляя возможность ленивым модулям иметь свои собственные сервисы, не создавая новые сервисы, которые должна-быть-синглтон.
Это соглашение дает вам замечательную возможность forChild
- использовать "сервисы только для лениво загруженных модулей".
Вот демка Root провайдеры выдают положительные числа, лениво загруженные модули выдают отрицательные числа.
Ответ 4
Если appRoutes содержит путь к различным функциям на сайте (admin crud, user crud, book crud) и мы хотим разделить их, мы можем просто сделать это:
imports: [
BrowserModule, HttpModule,
AppRoutingModule,
RouterModule.forRoot(categoriesRoutes),
RouterModule.forRoot(auteursRoutes),
],
И для маршрутов:
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},
]
Ответ 5
https://www.freelancermap.com/freelancer-tips/12255-forroot-forchild-angular
Это статья об использовании forRoot и forChild в Angular; Я не особо разбираюсь в этой теме, но, возможно, это может быть полезно!