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

112

Quelles sont les différences entre ces deux et quels sont les cas d'utilisation pour chacun?

Les documents ne sont pas vraiment utiles:

forRoot crée un module qui contient toutes les directives, les routes données et le service du routeur lui-même.

forChild crée un module qui contient toutes les directives et les routes données, mais n'inclut pas le service de routeur.

Ma vague supposition est que l'un est pour le module «principal» et l'autre pour tous les modules importés (puisqu'ils auraient déjà le service disponible à partir du module principal), mais je ne peux pas vraiment penser à un cas d'utilisation.

VSO
la source
1
Pourriez-vous être plus précis sur ce que vous ne comprenez pas? La citation que vous avez incluse vous indique littéralement quelle est la différence.
jonrsharpe
2
Je ne comprends pas quel est l'intérêt d'utiliser .forChild (). Quand voudrais-je les directives et les itinéraires sans le service? En attendant, veuillez répondre à la question que vous avez supprimée du message ...
VSO
18
Il ne devrait y en avoir qu'un RouterServicepour une seule application Angular2. forRootva initialiser ce service et l'enregistrer dans DI avec une configuration de route, tandis forChildque n'enregistrera que des configurations de route supplémentaires et dira à Angular2 de réutiliser le RouterServicequi forRoota créé.
Harry Ninh
@HarryNinh: Merci - c'est ce que je cherchais. Quand voudriez-vous enregistrer des itinéraires supplémentaires en dehors de l'enregistrement initial? Cela semble un peu idiot. Je suppose qu'il n'y a aucun moyen de créer des itinéraires de manière dynamique.
VSO
1
voir ceci par l'auteur de routeur angulaire victor.
nikhil mehta

Réponses:

124

Je suggère fortement de lire cet article:

Module avec prestataires

Lorsque vous importez un module, vous utilisez généralement une référence à la classe de module:

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

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

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

De cette façon, tous les fournisseurs enregistrés sur le module Aseront ajoutés à l'injecteur racine et disponibles pour l'ensemble de l'application.

Mais il existe un autre moyen d'enregistrer un module auprès de fournisseurs comme celui-ci:

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

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

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

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

Cela a les mêmes implications que le précédent.

Vous savez probablement que les modules chargés paresseusement ont leur propre injecteur. Supposons donc que vous souhaitiez vous inscrire AServicepour être disponible pour l'ensemble de l'application, mais que certains ne BServicesoient disponibles que pour les modules à chargement différé. Vous pouvez refactoriser votre module comme ceci:

@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

Désormais, BServiceil ne sera disponible que pour les modules enfants chargés tardivement et AServicesera disponible pour l'ensemble de l'application.

Vous pouvez réécrire ce qui précède en tant que module exporté comme ceci:

@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

En quoi est-ce pertinent pour RouterModule?

Supposons qu'ils soient tous deux accessibles à l'aide du même jeton:

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

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

Avec des configurations séparées lorsque vous demandez tokenà un module chargé paresseusement, vous obtiendrez BServiceexactement comme prévu.

RouterModule utilise un ROUTESjeton pour obtenir toutes les routes spécifiques à un module. Puisqu'il veut que les routes spécifiques au module chargé paresseusement soient disponibles dans ce module (analogues à notre BService), il utilise une configuration différente pour les modules enfants chargés paresseusement:

static forChild(routes: Routes): ModuleWithProviders {
    return {
        ngModule: RouterModule, 
        providers: [{provide: ROUTES, multi: true, useValue: routes}]
    };
}
Max Koretskyi
la source
3
donc en d'autres termes, nous devrions appeler forChild () dans les modules de fonctionnalités car forRoot () était déjà appelé dans le module racine et tous les services nécessaires ont été ajoutés. Donc, appeler à nouveau forRoot () conduira à des états imprévisibles?
Wachburn
7
@Wachburn, appeler forRootun module chargé différé créera de nouvelles instances de tous les services globaux dans l'injecteur de module chargé différé. Oui, cela conduira à des résultats imprévisibles. Lisez également cet article Éviter les confusions courantes avec les modules dans Angular
Max Koretskyi
1
@Willwsharp, pourquoi?
Max Koretskyi
1
Je comprends parfaitement cela. Mais quelle est la différence entre Routermodule.foRoot, VS Routermodule.forChild? forRoot & forChild ne sont que des méthodes statiques qui donnent un objet en retour basé sur la valeur passée. Donc, dans le module de l'application, pourquoi je ne peux pas utiliser forChild ?? pourquoi son erreur de lancement? Pourquoi je ne peux pas utiliser plusieurs forRoot?
Subhadeep
2
Parfois, il est tout simplement bon d'aller droit au but avec des réponses. la surcharge d'informations conduit la plupart du temps à plus de confusion.
Adépòjù Olúwáségun
33

Je pense que les réponses sont correctes mais je pense qu'il manque quelque chose.
La chose qui manque est "pourquoi et qu'est-ce que ça résout?".
OK commençons.

Commençons par mentionner quelques informations:

Tous les modules ont accès aux services racine.
Ainsi, même les modules chargés paresseusement peuvent utiliser un service fourni dans app.module.
Que se passera-t-il si un module chargé paresseusement se fournira un service que le module d'application a déjà fourni? il y aura 2 instances.
Ce n'est pas un problème mais c'est parfois le cas .
Comment pouvons-nous le résoudre? N'importez simplement pas un module avec ce fournisseur dans des modules chargés différés.

Fin de l'histoire.

C'était juste pour montrer que les modules chargés différés ont leur propre point d'injection (par opposition aux modules non chargés différés).

Mais que se passe-t-il lorsqu'un module partagé (!) A été déclaré providerset que ce module est importé par lazy and app.module ? Encore une fois, comme nous l'avons dit, deux cas.

Alors, comment pouvons-nous résoudre cela dans le module partagé POV? Nous avons besoin d'un moyen de ne pas utiliser providers:[]! Pourquoi? car ils seront automatiquement importés à la fois dans la consommation paresseux et app.module et nous ne le voulons pas, car nous avons vu que chacun aura une instance différente.

Eh bien, il s'avère que nous pouvons déclarer un module partagé qui n'aura pas providers:[], mais fournira quand même des fournisseurs (désolé :))

Comment? Comme ça :

entrez la description de l'image ici

Avis, pas de fournisseurs.

Mais

  • que se passera-t-il maintenant lorsque app.module importera le module partagé avec POV de service? RIEN.

  • que se passera-t-il maintenant lorsqu'un module paresseux importera le module partagé avec POV de service? RIEN.

Saisie du mécanisme manuel par convention:

Vous remarquerez que les fournisseurs sur les photos ont service1etservice2

Cela nous permet d'importer service2pour les modules chargés différés et service1pour les modules non différés. ( toux ... routeur .... toux )

BTW, personne ne vous empêche d'appeler forRootdans un module paresseux. mais vous aurez 2 instances car vous app.moduledevriez également le faire - alors ne le faites pas dans des modules paresseux.

Aussi - si les app.moduleappels forRoot(et personne n'appelle forchild) - c'est bien, mais l'injecteur racine n'aura que service1. (disponible pour toutes les applications)

Alors pourquoi en avons-nous besoin? Je dirais :

Il permet à un module partagé, de pouvoir diviser ses différents fournisseurs, d'être utilisé avec des modules impatients et des modules paresseux - via forRootet par forChildconvention. Je répète: convention

C'est tout.

ATTENDRE !! pas un seul mot sur singleton ?? alors pourquoi est-ce que je lis singleton partout?

Eh bien - c'est caché dans la phrase ci-dessus ^

Il permet à un module partagé, de pouvoir diviser ses différents fournisseurs, d'être utilisé avec des modules impatients et des modules paresseux - via forRoot et forChild .

La convention (!!!) lui permet d'être singleton - ou pour être plus précis - si vous ne suivez pas la convention - vous n'obtiendrez PAS de singleton.
Ainsi, si vous ne chargez que forRootdans le app.module , vous n'obtenez qu'une seule instance car vous ne devez l'appeler forRootque dans le app.module.
BTW - à ce stade, vous pouvez oublier forChild. le module chargé paresseusement ne devrait pas / ne sera pas appelé forRoot- vous êtes donc en sécurité dans le POV de singleton.

forRoot et forChild ne sont pas un paquet incassable - c'est juste qu'il est inutile d'appeler Root qui ne sera évidemment chargé que dans app.module sans donner la possibilité de modules paresseux, d'avoir leurs propres services, sans créer de nouveaux services-qui-devraient-être -singleton.

Cette convention vous donne une belle capacité appelée forChild- à consommer des "services uniquement pour les modules chargés paresseusement".

Voici une démo Les fournisseurs de racine donnent des nombres positifs, les modules chargés paresseusement donnent des nombres négatifs.

Royi Namir
la source
1
1. Qu'est-ce que POV ici? 2. Le stackblitz ne fonctionne plus.
Nicholas K le
@NicholasK ce stackblitz gâche toujours les choses après un certain temps. je vais essayer de télécharger une nouvelle version
Royi Namir
26

La documentation indique clairement quel est le but de cette distinction ici: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

Appelez forRoot uniquement dans le module d'application racine, AppModule. L'appeler dans n'importe quel autre module, en particulier dans un module à chargement différé, est contraire à l'intention et est susceptible de produire une erreur d'exécution.

N'oubliez pas d'importer le résultat; ne l'ajoutez à aucune autre liste @NgModule.

Chaque application a exactement un point de départ (racine) avec lequel le service de routage principal doit être initialisé forRoot, tandis que les routes pour des fonctionnalités "enfants" particulières doivent être enregistrées en plus avec forChild. Il est extrêmement utile pour les sous-modules et les modules chargés paresseusement qui n'ont pas à être chargés au démarrage de l'application, et comme @Harry Ninh l'a dit, on leur dit de réutiliser RouterService au lieu de l'enregistrement du nouveau service, ce qui peut provoquer une erreur d'exécution.

Marcin
la source
2
Ces documents semblent déplacés, v2.angular.io/docs/ts/latest/guide/…
PeterS
1

Si appRoutes contient le chemin vers diverses fonctions du site (admin crud, user crud, book crud) et que nous voulons les séparer, nous pourrions simplement le faire:

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

Et pour les itinéraires:

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



]
Hmaied Belkhiria
la source