RouterModule.forRoot (ROUTES) vs RouterModule.forChild (ROUTES)


111

Was sind die Unterschiede zwischen diesen beiden und was sind die Anwendungsfälle für jeden?

Die Dokumente sind nicht gerade hilfreich:

forRoot erstellt ein Modul, das alle Anweisungen, die angegebenen Routen und den Routerdienst selbst enthält.

forChild erstellt ein Modul, das alle Anweisungen und die angegebenen Routen enthält, jedoch nicht den Routerdienst.

Meine vage Vermutung ist, dass eines für das 'Haupt'-Modul und das andere für alle importierten Module ist (da der Dienst bereits vom Hauptmodul verfügbar wäre), aber ich kann mir keinen Anwendungsfall vorstellen.


1
Könnten Sie genauer sagen, was Sie nicht verstehen? Das Zitat, das Sie beigefügt haben, sagt Ihnen buchstäblich, was der Unterschied ist.
Jonrsharpe

2
Ich verstehe nicht, wozu .forChild () sinnvoll ist. Wann würde ich die Richtlinien und die Routen ohne den Dienst wollen? In der Zwischenzeit beantworten Sie bitte die Frage, die Sie aus dem Beitrag gelöscht haben ...
VSO

17
Es sollte nur eine RouterServicefür eine einzelne Angular2-Anwendung geben. forRootinitialisiert diesen Dienst und registriert ihn zusammen mit einigen forChildRoutenkonfigurationen bei DI, registriert jedoch nur zusätzliche Routenkonfigurationen und weist Angular2 an RouterService, forRootdie erstellten erneut zu verwenden.
Harry Ninh

@ HarryNinh: Danke - das habe ich gesucht. Wann möchten Sie jedoch zusätzliche Routen außerhalb der Erstregistrierung registrieren? Scheint irgendwie albern. Ich vermute, es gibt keine Möglichkeit, Routen dynamisch zu erstellen.
VSO

1
siehe dies von Angular Router Autor Victor.
Nikil Mehta

Antworten:


123

Ich empfehle dringend, diesen Artikel zu lesen:

Modul mit Anbietern

Wenn Sie ein Modul importieren, verwenden Sie normalerweise einen Verweis auf die Modulklasse:

@NgModule({
    providers: [AService]
})
export class A {}

-----------------------------------

@NgModule({
    imports: [A]
})
export class B

Auf diese Weise werden alle im Modul registrierten Anbieter Azum Root-Injektor hinzugefügt und sind für die gesamte Anwendung verfügbar.

Es gibt aber noch eine andere Möglichkeit, ein Modul bei Anbietern wie diesem zu registrieren:

@NgModule({
    providers: [AService]
})
class A {}

export const moduleWithProviders = {
    ngModule: A,
    providers: [AService]
};

----------------------

@NgModule({
    imports: [moduleWithProviders]
})
export class B

Dies hat die gleichen Auswirkungen wie die vorherige.

Sie wissen wahrscheinlich, dass faul geladene Module einen eigenen Injektor haben. Angenommen, Sie möchten sich registrieren AService, um für die gesamte Anwendung BServiceverfügbar zu sein , einige jedoch nur für faul geladene Module. Sie können Ihr Modul folgendermaßen umgestalten:

@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

Jetzt ist BServicees nur für untergeordnete, faul geladene Module AServiceverfügbar und für die gesamte Anwendung.

Sie können das oben genannte Modul wie folgt als exportiertes Modul umschreiben:

@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

Wie ist das für RouterModule relevant?

Angenommen, auf beide wird mit demselben Token zugegriffen:

export const moduleWithProvidersForRoot = {
    ngModule: A,
    providers: [{provide: token, useClass: AService}]
};

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [{provide: token, useClass: BService}]
};

Mit separaten Konfigurationen erhalten Sie, wenn Sie tokenvon einem faul geladenen Modul anfordern , BServicegenau wie geplant.

RouterModule verwendet ROUTESToken, um alle für ein Modul spezifischen Routen abzurufen . Da in diesem Modul spezifische Routen für verzögert geladene Module verfügbar sein sollen (analog zu unserem BService), wird für die verzögert geladenen untergeordneten Module eine andere Konfiguration verwendet:

static forChild(routes: Routes): ModuleWithProviders {
    return {
        ngModule: RouterModule, 
        providers: [{provide: ROUTES, multi: true, useValue: routes}]
    };
}

3
Mit anderen Worten, wir sollten forChild () in Feature-Modulen aufrufen, da forRoot () bereits im Root-Modul aufgerufen wurde und alle erforderlichen Services hinzugefügt wurden. Ein erneuter Aufruf von forRoot () führt also zu unvorhersehbaren Zuständen?
Wachburn

7
@Wachburn: Wenn Sie forRootein verzögert geladenes Modul aufrufen, werden neue Instanzen aller globalen Dienste im Injektor für verzögert geladene Module erstellt. Ja, dies führt zu unvorhersehbaren Ergebnissen. Lesen Sie auch diesen Artikel Vermeiden Sie häufige Verwechslungen mit Modulen in Angular
Max Koretskyi

1
@ Willwsharp, warum?
Max Koretskyi

1
Ich verstehe das vollkommen. Aber was ist der Unterschied zwischen Routermodule.foRoot und VS Routermodule.forChild? forRoot & forChild sind nur statische Methoden, die ein Objekt basierend auf dem übergebenen Wert zurückgeben. Warum kann ich im App-Modul forChild nicht verwenden? Warum ist es ein Wurffehler? Warum kann ich nicht mehrere forRoot verwenden?
Subhadeep

2
Manchmal ist es einfach gut, mit Antworten direkt auf den Punkt zu kommen. Informationsüberflutung führt meistens zu mehr Verwirrung.
Adépòjù Olúwáségun

33

Ich denke, die Antworten sind richtig, aber ich denke, etwas fehlt.
Was fehlt, ist "warum und was es löst?".
In Ordnung, lass uns anfangen.

Lassen Sie uns zuerst einige Informationen erwähnen:

Alle Module haben Zugriff auf die Root-Dienste.
So können auch faul geladene Module einen Dienst nutzen, der in bereitgestellt wurde app.module.
Was passiert, wenn sich ein faul geladenes Modul selbst einen Dienst bereitstellt, den das App-Modul bereits bereitgestellt hat? Es wird 2 Instanzen geben.
Es ist kein Problem, aber manchmal ist es .
Wie können wir es lösen? Importieren Sie einfach kein Modul mit diesem Anbieter in faul geladene Module.

Ende der Geschichte.

Dies soll nur zeigen, dass faul geladene Module einen eigenen Injektionspunkt haben (im Gegensatz zu nicht faul geladenen Modulen).

Aber was passiert, wenn ein freigegebenes (!) Modul deklariert providerswurde und dieses Modul von faul und importiert wird app.module? Wieder, wie gesagt, zwei Fälle.

Wie können wir dies im gemeinsam genutzten Modul POV lösen? Wir brauchen einen Weg, um nicht zu benutzen providers:[]! Warum? weil sie automatisch in Lazy und App.module importiert werden und wir das nicht wollen, da wir gesehen haben, dass jede eine andere Instanz hat.

Nun, es stellt sich heraus, dass wir ein gemeinsam genutztes Modul deklarieren können providers:[], das keine Anbieter hat , aber dennoch Anbieter bereitstellt (sorry :))

Wie? So was :

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, keine Anbieter.

Aber

  • Was passiert jetzt, wenn app.module das gemeinsam genutzte Modul mit POV of Service importiert? NICHTS.

  • Was passiert jetzt, wenn ein Lazy-Modul das gemeinsam genutzte Modul mit POV of Service importiert? NICHTS.

Eingabe des manuellen Mechanismus über Konvention:

Sie werden feststellen, dass die Anbieter auf den Bildern service1und habenservice2

Dies ermöglicht uns den Import service2für träge geladene Module und service1für nicht faul geladene Module. ( Husten ... Router ... Husten )

Übrigens, niemand hält Sie davon ab, forRootinnerhalb eines faulen Moduls anzurufen . Aber Sie werden 2 Instanzen haben, weil Sie app.modulees auch tun sollten - also tun Sie es nicht in faulen Modulen.

Auch - wenn app.moduleAnrufe forRoot(und niemand ruft forchild) - ist das in Ordnung, aber Root-Injektor wird nur haben service1. (für alle App verfügbar)

Warum brauchen wir es also? Ich würde sagen :

Es ermöglicht einem gemeinsam genutzten Modul, seine verschiedenen Anbieter aufzuteilen , um mit eifrigen Modulen und faulen Modulen verwendet zu werden - via forRootund forChildConvention. Ich wiederhole: Konvention

Das ist es.

WARTEN !! kein einziges Wort über Singleton? Warum lese ich überall Singleton?

Nun - es ist im obigen Satz versteckt ^

Es ermöglicht ein gemeinsames Modul in der Lage sein zu spalten seinen verschiedenen Anbieter mit eifrigen Modulen und faul Module verwendet werden - über forRoot und forChild .

Die Konvention (!!!) erlaubt es, Singleton zu sein - oder genauer gesagt - wenn Sie der Konvention nicht folgen, erhalten Sie KEINEN Singleton.
Also , wenn Sie nur laden forRootin der app.module , dann bekommt man nur eine Instanz , weil man nur nennen sollte forRootes in der app.module.
Übrigens - an dieser Stelle können Sie vergessen forChild. Das faul geladene Modul sollte / wird nicht aufrufen forRoot- Sie sind also in POV von Singleton sicher.

forRoot und forChild sind kein unzerbrechliches Paket - es gibt nur keinen Grund, Root aufzurufen, das offensichtlich nur geladen wird, app.module ohne die Fähigkeit für faule Module zu haben, ihre eigenen Dienste zu haben, ohne neue Dienste zu erstellen, die sein sollten -singleton.

Diese Konvention forChildbietet Ihnen eine nette Möglichkeit , "Dienste nur für faul geladene Module" zu nutzen.

Hier ist eine Demo Root-Anbieter liefert positive Zahlen, faul geladene Module ergeben negative Zahlen.


1. Was ist POV hier? 2. Der Stackblitz funktioniert nicht mehr.
Nicholas K

@NicholasK Dieser Stackblitz bringt nach einer Weile immer alles durcheinander. Ich werde versuchen, eine neue Version hochzuladen
Royi Namir

26

In der Dokumentation wird klar angegeben, wozu diese Unterscheidung hier dient: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

Rufen Sie forRoot nur im Root-Anwendungsmodul AppModule auf. Das Aufrufen in einem anderen Modul, insbesondere in einem verzögert geladenen Modul, widerspricht der Absicht und führt wahrscheinlich zu einem Laufzeitfehler.

Denken Sie daran, das Ergebnis zu importieren. Fügen Sie es keiner anderen @ NgModule-Liste hinzu.

Jede Anwendung hat genau einen Startpunkt (root), mit dem der Hauptroutingdienst initialisiert werden soll forRoot, während Routen für bestimmte "untergeordnete" Funktionen zusätzlich registriert werden sollten forChild. Dies ist äußerst nützlich für Submodule und verzögert geladene Module, die beim Start der Anwendung nicht geladen werden müssen. Wie @Harry Ninh sagte, werden sie aufgefordert, RouterService anstelle der Registrierung des neuen Dienstes wiederzuverwenden, was zu einem Laufzeitfehler führen kann.


2
Diese Dokumente scheinen verschoben zu sein, v2.angular.io/docs/ts/latest/guide/…
PeterS

1

Wenn appRoutes einen Pfad zu verschiedenen Funktionen auf der Site enthält (Admin Crud, User Crud, Book Crud) und wir sie trennen möchten, können wir dies einfach tun:

 imports: [
    BrowserModule, HttpModule,
    AppRoutingModule,
    RouterModule.forRoot(categoriesRoutes),
    RouterModule.forRoot(auteursRoutes),
  ],

Und für Routen:

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},



]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.