167 votes

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

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

Le site docs ne sont pas vraiment utiles :

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

forChild crée un module qui contient toutes les directives et les itinéraires donnés, mais ne comprend pas le service de routage.

Je pense vaguement que l'un est destiné au module "principal" et l'autre aux modules importés (puisqu'ils disposent déjà du service disponible dans le module principal), mais je ne vois pas vraiment de cas d'utilisation.

1 votes

Pourriez-vous être plus précis sur ce que vous ne comprenez pas ? La citation que vous avez incluse vous dit littéralement quelle est la différence.

3 votes

Je ne comprends pas l'intérêt d'utiliser .forChild(). Quand voudrais-je avoir les directives et les routes sans le service ? En attendant, veuillez répondre à la question que vous avez supprimée du message...

39 votes

Il ne devrait y avoir qu'un seul RouterService pour une seule application Angular2. forRoot va initialiser ce service et l'enregistrer auprès de DI avec une configuration de route, tandis que forChild ne fera qu'enregistrer des configurations de route supplémentaires et indiquera à Angular2 de réutiliser la configuration de la route. RouterService que forRoot a créé.

168voto

Maximus Points 1342

Je vous conseille vivement de lire cet article :

Module avec les fournisseurs

Lorsque vous importez un module, vous utilisez généralement une référence à la classe du 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 A sera ajouté à l'injecteur Root et disponible pour l'ensemble de l'application.

Mais il existe une autre façon 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 paresseusement ont leur propre injecteur. Supposons donc que vous souhaitiez enregistrer AService d'être disponible pour l'ensemble de l'application, mais certains BService pour qu'il ne soit disponible que pour les modules chargés paresseusement. Vous pouvez remanier votre module comme suit :

@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

Maintenant BService ne sera disponible que pour les modules enfants chargés paresseusement, 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

En quoi cela concerne-t-il RouterModule ?

Supposons qu'on y accède tous les deux 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 le demandez token à partir d'un module chargé paresseusement, vous obtiendrez BService comme prévu.

RouterModule utilise ROUTES pour obtenir toutes les routes spécifiques à un module. Comme il veut que les routes spécifiques au module chargé paresseusement soient disponibles à l'intérieur de 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}]
    };
}

7 votes

En d'autres termes, nous devrions appeler forChild() dans les modules fonctionnels parce que forRoot() a déjà été appelé dans le module Root et que tous les services nécessaires ont été ajoutés. Donc, appeler forRoot() à nouveau conduira à des états imprévisibles ?

11 votes

@Wachburn, appel forRoot dans un module chargé paresseusement créera de nouvelles instances de tous les services globaux dans l'injecteur de module chargé paresseusement. Oui, cela conduira à des résultats imprévisibles. Lisez également cet article Éviter les confusions courantes avec les modules en Angular

0 votes

Cette réponse ne devrait-elle pas contenir un avertissement indiquant que l'auteur de l'article lié est le même que l'auteur de cette réponse ?

42voto

Royi Namir Points 34397

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

Tout d'abord, mentionnons quelques informations :

Tous les modules ont accès aux services de la racine.
Ainsi, même les modules chargés paresseusement peuvent utiliser un service qui a été fourni en app.module .
Que se passera-t-il si un module chargé paresseusement se fournit un service que le module d'application a déjà fourni ? 2 instances.
Ce n'est pas un problème mais parfois c'est .
Comment pouvons-nous résoudre ce problème ? Il suffit de ne pas importer un module avec ce fournisseur pour les modules chargés paresseusement.

Fin de l'histoire.

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

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

Comment pouvons-nous résoudre ce problème dans le cadre du module partagé ? pas à utiliser providers:[] ! Pourquoi ? parce qu'ils seront importés automatiquement dans consuming lazy 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 de providers:[] mais quand même, fournira des prodiges ( désolé :))

Comment ? Comme ceci :

enter image description here

Avis, aucun fournisseur.

Mais

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

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

Entrée du mécanisme manuel par convention :

Vous remarquerez que les fournisseurs figurant sur les photos ont service1 et service2

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

BTW, personne ne vous empêche d'appeler. forRoot dans un module paresseux, mais vous aurez 2 instances car app.module devrait également le faire - donc Ne le fais pas. le faire en modules paresseux.

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

Alors pourquoi en avons-nous besoin ? Je dirais :

Il permet à un module partagé, d'être capable de divisé son différents fournisseurs à utiliser avec des modules avides et des modules paresseux. via forRoot et forChild convention. 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é, d'être capable de divisé son différents fournisseurs à utiliser avec des modules avides et des modules paresseux. via pourRoot et pourChild .

Le site convention ( !!!) lui permet d'être singleton - ou pour être plus précis - si vous ne suivez pas la convention - vous serez PAS obtenir un singleton.
Donc si vous ne chargez que forRoot dans le app.module alors vous n'obtiendrez qu'une seule instance car vous ne devez appeler que forRoot dans le app.module .
BTW - à ce stade, vous pouvez oublier de forChild . le module chargé paresseusement ne devrait pas / ne veut pas appeler forRoot - donc vous êtes en sécurité dans le POV du singleton.

forRoot et forChild ne sont pas un seul paquet insécable - c'est juste qu'il n'y a pas de raison d'appeler Root qui ne sera évidemment chargé que dans l'application app.module sans donner la possibilité aux modules paresseux d'avoir leurs propres services, sans créer de nouveaux services qui devraient être des squelettes.

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

Voici une démonstration Les fournisseurs de racines donnent des chiffres positifs, les modules chargés paresseusement donnent des chiffres négatifs.

1 votes

1. Qu'est-ce que le POV ici ? 2. Le stackblitz ne fonctionne plus.

0 votes

@NicholasK ce stackblitz fait toujours des erreurs au bout d'un moment. Je vais essayer de mettre en ligne une nouvelle version.

0 votes

@RoyiNamir Comme vous l'avez mentionné, les modules chargés paresseusement accèdent également aux fournisseurs des modules racines, alors pourquoi devons-nous les ajouter à nouveau dans les modules paresseux ? Même dans le cas du module partagé, si nous l'avons ajouté dans le module racine, pourquoi ajouter un module partagé dans le module chargé paresseusement ? Tous les fournisseurs qui ne sont pas présents dans le module racine mais qui sont nécessaires dans le module paresseux, je peux les ajouter séparément dans les modules paresseux, non ? Je ne sais toujours pas comment les utiliser.

30voto

Marcin Points 986

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 tout autre module, en particulier dans un module à chargement paresseux, 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 pas à une autre liste @NgModule.

Chaque application a exactement un point de départ (Root) où le service de routage principal doit être initialisé avec forRoot tandis que les routes pour des fonctions "enfants" particulières doivent être enregistrées en plus auprès de forChild . C'est extrêmement utile pour les submodules et les modules à chargement paresseux qui ne doivent 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 d'enregistrer le nouveau service, ce qui peut provoquer une erreur d'exécution.

2 votes

Ces documents semblent avoir été déplacés, v2.angular.io/docs/ts/latest/guide/

2voto

Si appRoutes contient les chemins d'accès aux différentes fonctions du site (admin crud, user crud, book crud) et que nous voulons les séparer, nous pouvons simplement le faire :

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

Et pour les routes :

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

]

2voto

Heshan Points 108

Considérez ceci comme l'implémentation du module routeur pour une meilleure compréhension. Les routes paresseuses et les routes communes doivent être gérées séparément, en supportant le chargement paresseux.

   @NgModule({
      declarations: [

      ],
      exports: [],
    })
    export class RouteModule {
      static forRoot(): ModuleWithProviders<RouteModule> {
        return { ngModule: RouteModule, providers: [routerHistoryService,handleCommonRoutesService] };
      }

      static forChild(): ModuleWithProviders<RouteModule> {
        return {
          ngModule: RouteModule, providers: [handleChildRouterService]
        };
      }
    }

pourroot -> ajoutera le module routerModule avec les fournisseurs (services), ainsi tous les services nécessaires pour le routage angulaire commun (exemple routerHistoryService ) seront injectés dans l'injecteur Root à partir du module d'application (module Root).

pour l'enfant -> ajoutera le routerModule avec les providers(services) ,mais comme les services communs(ex routerHistoryService ) déjà ajoutés dans l'injecteur Root Root, à partir des modules chargés paresseusement, nous pouvons les utiliser et il n'est pas nécessaire de les ajouter à nouveau. Si nous les ajoutons, cela créera deux instances. mais il pourrait y avoir des services, spécifiquement nécessaires pour gérer les Dans ce cas, lorsque nous appelons le forchild, nous pouvons les fournir (ex. :handleChildRouterService)

si le routerModule si nous n'implémentons pas forRoot et forChild Considérez le scénario suivant

1)dans les injecteurs racine routerHistoryService la route précédente est "home/lazymodule"

2)mais dans le module paresseux avec le nouveau service d'historique ,la route précédente est nulle.

donc il n'a pas de données pour revenir en arrière lorsque le bouton retour est cliqué. C'est pourquoi routerModule a été implémenté en suivant ce modèle pour s'assurer que le module routeur n'a qu'une seule instance dans toute l'application

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X