RouterModule.forRoot (ROUTES) - RouterModule.forChild (ROUTES)


111

Які відмінності між цими двома та якими є випадки використання для кожного?

Ці документи не зовсім корисні:

forRoot створює модуль, який містить усі директиви, задані маршрути та саму послугу маршрутизатора.

forChild створює модуль, який містить усі директиви та задані маршрути, але не включає послугу маршрутизатора.

Моя неясна здогадка полягає в тому, що один призначений для "головного" модуля, а інший - для будь-яких імпортних модулів (оскільки вони вже мали б доступну послугу з головного модуля), але я не можу реально придумати випадок використання.


1
Чи можете ви бути більш конкретними щодо того, чого ви не розумієте? Цитата, яку ви включили, буквально говорить про те, у чому різниця.
jonrsharpe

2
Я не розумію, в чому сенс використання .forChild (). Коли я хочу отримати директиви та маршрути без служби? Тим часом, будь ласка, дайте відповідь на питання, яке ви видалили з поста ...
VSO

18
Для RouterServiceоднієї програми Angular2 має бути лише одна . forRootинициализирует цю послугу і зареєструвати його в DI разом з якою - то маршрут конфігурації, в той час як forChildтільки зареєструвати додаткові конфіги маршрут і сказати Angular2 , щоб повторно використовувати , RouterServiceщо forRootстворив.
Гаррі Нінь

@HarryNinh: Спасибі - ось що я шукав. Коли ви хочете зареєструвати додаткові маршрути поза початковою реєстрацією? Здається, нерозумно. Я здогадуюсь, немає способу створити маршрути динамічно.
VSO

1
дивіться це за допомогою кутового маршрутизатора автора-переможця.
nikhil mehta

Відповіді:


123

Я настійно пропоную прочитати цю статтю:

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

Під час імпорту модуля зазвичай використовується посилання на клас модуля:

@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 () вже був викликаний в кореневому модулі і всі необхідні сервіси були додані. Тож запрошення доRoot () знову призведе до непередбачуваних станів?
Вахберн

7
@Wachburn, виклик forRootв ледачому завантаженому модулі створить нові екземпляри всіх глобальних служб в інжекторі ледачого завантаженого модуля. Так, це призведе до непередбачуваних результатів. Читайте також цю статтю Уникнення загальних плутань з модулями у кутовому
Макс Корецький

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в межах ледачого модуля. але у вас буде два екземпляри, тому що ви app.moduleтакож повинні це робити, тому не робіть цього в ледачих модулях.

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

То навіщо нам це потрібно? Я б сказав:

Це дозволяє спільному модулю мати змогу розділити своїх різних постачальників для використання з нетерплячими модулями та ледачими модулями - через forRootта forChildз'їзд. Повторюю: умовність

Це воно.

ЗАЧЕКАЙТЕ !! ні одного слова про одиночку ?? так чому я читаю одиночну всюди?

Ну - це приховано у реченні вище ^

Це дозволяє спільному модулю мати можливість розділити своїх різних постачальників, щоб їх використовувати з нетерплячими модулями та ледачими модулями - через forRoot та forChild .

Конвенції (!!!) дозволяє йому бути Сінглтон - або , щоб бути більш точним - якщо ви не будете дотримуватися угоди - ви НЕ отримаєте Сінглтон.
Отже, якщо ви завантажуєте лише forRootв app.module , тоді ви отримуєте лише один екземпляр, тому що ви повинні викликати forRootйого лише в app.module.
BTW - у цей момент можна забути forChild. ледачий завантажений модуль не повинен / не дзвонить forRoot- значить, ви в безпеці в POV одиночної.

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

Ця умова дає вам приємну можливість forChild- споживати "послуги лише для ледачих завантажених модулів".

Ось демонстрація Root постачальників дає позитивні цифри, ледачі завантажені модулі дають негативні числа.


1. Що тут POV? 2. Stackblitz більше не працює.
Микола К

@NicholasK цей стекбліт завжди через деякий час псує речі. я спробую завантажити нову версію
Royi Namir

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 містить шлях до різних функцій на сайті (адміністратор crud, користувач 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},



]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.