RouterModule.forRoot (МАРШРУТЫ) против RouterModule.forChild (МАРШРУТЫ)

113

В чем разница между этими двумя и каковы варианты использования каждого из них?

Эти документы не совсем полезны:

forRoot создает модуль, содержащий все директивы, указанные маршруты и сам сервис маршрутизатора.

forChild создает модуль, содержащий все директивы и заданные маршруты, но не включающий службу маршрутизатора.

Я смутно предполагаю, что один предназначен для «основного» модуля, а другой - для любых импортированных модулей (поскольку они уже будут иметь службу, доступную из основного модуля), но я действительно не могу придумать вариант использования.

VSO
источник
1
Не могли бы вы подробнее рассказать о том, чего не понимаете? Цитата, которую вы включили, буквально говорит вам, в чем разница.
jonrsharpe
2
Я не понимаю, в чем смысл использования .forChild (). Когда мне понадобятся директивы и маршруты без службы? А пока ответьте на вопрос, который вы удалили из сообщения ...
VSO
19
Должен быть только один RouterServiceдля одного приложения Angular2. forRootинициализирует эту услугу и зарегистрировать его в DI вместе с какой - то маршрут конфигурации, в то время как forChildтолько зарегистрировать дополнительные конфиги маршрут и сказать Angular2 , чтобы повторно использовать , RouterServiceчто forRootсоздал.
Гарри Нинь
@HarryNinh: Спасибо - это то, что я искал. Но когда вы захотите зарегистрировать дополнительные маршруты помимо первоначальной регистрации? Кажется глупым. Я предполагаю, что нет возможности динамически создавать маршруты.
VSO
1
см. это автор углового маршрутизатора победитель.
nikhil mehta

Ответы:

125

Я настоятельно рекомендую прочитать эту статью:

Модуль с провайдерами

Когда вы импортируете модуль, вы обычно используете ссылку на класс модуля:

@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}]
    };
}
Макс Корецкий
источник
3
Другими словами, мы должны вызывать forChild () в функциональных модулях, потому что forRoot () уже был вызван в корневом модуле и были добавлены все необходимые службы. Значит, повторный вызов forRoot () приведет к непредсказуемым состояниям?
Wachburn
7
@Wachburn, вызов forRootмодуля с отложенной загрузкой создаст новые экземпляры всех глобальных служб в инжекторе модуля с отложенной загрузкой. Да, это приведет к непредсказуемым результатам. Также прочтите эту статью Как избежать общей путаницы с модулями в Angular
Макс Корецкий
1
@Willwsharp, почему?
Макс Корецкий
1
Я это полностью понимаю. Но в чем разница между Routermodule.foRoot, VS Routermodule.forChild? forRoot и forChild - это просто статический метод, который возвращает объект в зависимости от переданного значения. Итак, в модуле приложения, почему я не могу использовать forChild ?? почему его ошибка бросания? Почему я не могу использовать несколько forRoot?
Subhadeep
2
Иногда лучше сразу перейти к делу с ответами. информационная перегрузка в большинстве случаев приводит к еще большей путанице.
Adépòjù Olúwáségun
33

Я думаю, что ответы верны, но я думаю, что чего-то не хватает.
Не хватает того, «почему и что это решает?».
Хорошо, давайте начнем.

Сначала упомянем некоторую информацию:

Все модули имеют доступ к корневым службам.
Таким образом, даже ленивые модули могут использовать службу, которая была предоставлена ​​в app.module.
Что произойдет, если ленивый загруженный модуль предоставит себе услугу, которую уже предоставил модуль приложения? будет 2 экземпляра.
Это не проблема, но иногда бывает .
Как мы можем это решить? просто не импортируйте модуль с этим поставщиком в модули с отложенной загрузкой.

Конец истории.

Это ^ было просто для того, чтобы показать, что модули с ленивой загрузкой имеют свою собственную точку внедрения (в отличие от модулей с ленивой загрузкой).

Но что произойдет, если объявлен разделяемый (!) Модуль providers, и этот модуль будет импортирован ленивым и app.module ? Опять же, как мы уже сказали, два случая.

Итак, как мы можем решить эту проблему в общем модуле POV? Нам нужен способ не использовать providers:[]! Зачем? потому что они будут автоматически импортированы как в ленивый, так и в app.module, и мы этого не хотим, поскольку мы видели, что у каждого будет свой экземпляр.

Что ж, получается, что мы можем объявить общий модуль, который не будет иметь providers:[], но все же предоставит провайдеров (извините :))

Как? Как это :

введите описание изображения здесь

Заметьте, нет провайдеров.

Но

  • что будет теперь, когда app.module импортирует общий модуль с POV сервиса? НИЧЕГО.

  • что будет теперь, когда ленивый модуль импортирует общий модуль с POV службы? НИЧЕГО.

Ввод ручного механизма через соглашение:

Вы заметите, что у провайдеров на картинках есть service1иservice2

Это позволяет нам импортировать service2как ленивые, так и service1неленивые модули. ( кашель ... роутер .... кашель )

Кстати, никто не мешает вам звонить forRootв ленивом модуле. но у вас будет 2 экземпляра, потому app.moduleчто это тоже должно быть - так что не делайте этого в ленивых модулях.

Также - если app.moduleзвонит forRoot(а никто не звонит forchild) - нормально, но root-инжектор будет только service1. (доступно для всех приложений)

Так зачем нам это нужно? Я бы сказал :

Это позволяет разделяемому модулю иметь возможность разделить своих разных поставщиков для использования с нетерпеливыми модулями и ленивыми модулями - через forRootи forChildсоглашение. Повторяю: условность

Вот и все.

ПОДОЖДИТЕ !! ни слова про синглтон ?? так почему я везде читаю синглтон?

Что ж - это скрыто в предложении выше ^

Это позволяет разделяемому модулю иметь возможность разделить своих разных поставщиков для использования с нетерпеливыми модулями и ленивыми модулями - через forRoot и forChild .

Конвенции (!!!) позволяет ему быть синглтон - или , чтобы быть более точным - если вы не будете следовать соглашению - вы НЕ получите синглтон.
Итак, если вы загружаете только forRootв app.module , то вы получаете только один экземпляр, потому что вы должны вызывать forRootего только в app.module.
Кстати - на этом этапе можно забыть forChild. ленивый загруженный модуль не должен / не будет вызывать forRoot- так что вы в безопасности в POV синглтона.

forRoot и forChild - это не один нерушимый пакет - просто нет смысла вызывать Root, который, очевидно, будет загружен только в, app.module без предоставления возможности для ленивых модулей, иметь свои собственные службы, без создания новых служб, которые должны быть -одинарный.

Это соглашение дает вам прекрасную возможность, называемую forChild- потреблять «услуги только для ленивых загружаемых модулей».

Вот демо Root-провайдеры дают положительные числа, ленивые загруженные модули дают отрицательные числа.

Ройи Намир
источник
1
1. Что здесь такое POV? 2. stackblitz больше не работает.
Николас К.
@NicholasK, этот стекблиц всегда портит вещи через некоторое время. я постараюсь загрузить новую версию
Рой Намир
26

В документации четко указано, какова цель этого различия здесь: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

Вызывайте forRoot только в корневом модуле приложения, AppModule. Вызов его в любом другом модуле, особенно в ленивом загружаемом модуле, противоречит намерению и может привести к ошибке времени выполнения.

Не забудьте импортировать результат; не добавляйте его ни в какой другой список @NgModule.

Каждое приложение имеет ровно одну начальную точку (корень), в которой должна быть инициализирована основная служба маршрутизации forRoot, а маршруты для определенных «дочерних» функций должны регистрироваться дополнительно forChild. Это чрезвычайно полезно для подмодулей и лениво загружаемых модулей, которые не нужно загружать при запуске приложения, и, как сказал @Harry Ninh, им предлагается повторно использовать RouterService вместо регистрации новой службы, что может вызвать ошибку времени выполнения.

Марчин
источник
2
Эти документы, похоже, перемещены, v2.angular.io/docs/ts/latest/guide/…
PeterS
1

Если 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},



]
Хмайед Белхирия
источник