web-dev-qa-db-fra.com

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

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

Les docs ne sont pas vraiment utiles:

forRoot crée un module contenant toutes les directives, les itinéraires donnés et le service de routeur lui-même.

forChild crée un module contenant toutes les directives et les itinéraires donnés, mais n'inclut pas le service de routeur.

Mon vague conjecture est que l'un concerne le module "principal" et l'autre, tous les modules importés (car ils auraient déjà le service disponible à partir du module principal), mais je ne peux pas vraiment penser à un cas d'utilisation.

78
VSO

Je suggère fortement de lire cet article:

Module avec les fournisseurs

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

Ainsi, tous les fournisseurs inscrits sur le module A seront ajoutés à l'injecteur racine et disponibles pour l'ensemble de l'application.

Mais il existe un autre moyen d’enregistrer un module avec des 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 paresseux ont leur propre injecteur. Supposons donc que vous souhaitiez enregistrer AService pour qu'il soit disponible pour l'ensemble de l'application, mais que certains BService ne soient disponibles que pour les modules chargés paresseux. 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, BService ne sera disponible que pour les modules chargés par enfant et AService sera disponible pour l'ensemble de l'application.

Vous pouvez réécrire ce qui précède comme un 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

Comment est-ce pertinent pour RouterModule?

Supposons qu'ils soient tous deux accessibles en utilisant le 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 à partir d'un module chargé paresseux, vous obtenez BService exactement comme prévu.

RouterModule utilise le jeton ROUTES pour obtenir toutes les routes spécifiques à un module. Puisqu'il souhaite que des itinéraires spécifiques au module chargé paresseux soient disponibles dans ce module (analogues à notre service BService), il utilise une configuration différente pour les modules enfants chargés paresseux:

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

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 pourRoot uniquement dans le module d'application racine, AppModule. L'appeler dans un autre module, en particulier dans un module chargé paresseux, est contraire à l'intention et est susceptible de générer une erreur d'exécution.

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

Chaque application a exactement un point de départ (racine) où le service de routage principal doit être initialisé avec forRoot, tandis que les itinéraires de fonctions "enfants" particulières doivent être enregistrés en plus de forChild. Il est extrêmement utile pour les sous-modules et les modules chargés paresseux qui ne doivent pas être chargés au démarrage de l'application. Comme @Harry Ninh l'a dit, il est dit de réutiliser RouterService au lieu de l'enregistrement du nouveau service, ce qui peut entraîner une erreur d'exécution.

24
Marcin

Je pense que les réponses sont bonnes mais je pense qu'il manque quelque chose.
Ce qui manque, c’est "pourquoi et ce qu’il résout?".
OK commençons.

D'abord mentionnons quelques informations:

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

Fin de l'histoire.

C'était simplement pour montrer que les modules chargés paresseux avaient leur propre point d'injection (par opposition aux modules non chargés paresseux).

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

Alors, comment pouvons-nous résoudre ce problème dans le module partagé POV? nous avons besoin d'un moyen pas d'utiliser providers:[]! Pourquoi ? Parce qu'ils seront automatiquement importés à la fois par la consommation et app.module et nous ne voulons pas cela, 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 qui fournira quand même des prodivers (désolé :)

Comment ? Comme ça :

enter image description here

Avis, pas de fournisseurs.

Mais

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

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

Saisie du mécanisme manuel via convention:

Vous remarquerez que les fournisseurs dans les images ont service1 et service2

Cela nous permet d'importer service2 pour les modules chargés paresseux et service1 pour les modules non paresseux. ( toux ... routeur .... toux )

En passant, personne ne vous empêche d'appeler forRoot dans un module différé. mais vous aurez 2 instances car app.module devrait également le faire - donc ne le faites pas dans des modules différés.

De plus, si app.module appelle 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 pour être utilisé avec des modules rapides et des modules différés - via forRoot et forChild convention. Je répète: convention

Voilà.

WAIT !! Pas un mot à propos de 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 pour être utilisé avec des modules rapides et des modules différés - via pourRoot et forChild .

La convention (!!!) lui permet d'être singleton - ou pour être plus précis - si vous ne suivez pas la convention - vous allez PAS obtenir un singleton.
Donc, si vous ne chargez que forRoot dans le app.module, vous n’obtenez qu’une seule instance, car vous ne pouvez l’appeler que par forRoot dans le app.module.
BTW - à ce stade, vous pouvez oublier forChild. le module chargé paresseux ne devrait pas appeler/ne s'appellera pas forRoot - vous êtes donc en sécurité au POV de singleton.

forRoot et forChild ne sont pas un paquet incassable - c'est juste qu'il n'y a aucun intérêt à faire appel à Root qui ne sera évidemment chargé que dans app.module sans donner la possibilité d'utiliser des modules paresseux, d'avoir leurs propres services, sans créer de nouveaux services- qui-devrait-être-singleton.

Cette convention vous donne une capacité de Nice appelée forChild - de consommer des "services uniquement pour les modules chargés paresseux".

Voici une démonstration Les fournisseurs de racines génèrent des nombres positifs, les modules chargés paresseux génèrent des nombres négatifs.

23
Royi Namir

Si appRoutes contient le chemin d'accès à diverses fonctions du site (admin crud, utilisateur crud, book crud) et que nous souhaitons 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},



]
0
Hmaied Belkhiria