web-dev-qa-db-fra.com

Comment router vers un module en tant qu'enfant d'un module - Angular 2 RC 5

Je suis en train de mettre à niveau une application sur laquelle je travaille vers la dernière version candidate Angular 2. Dans le cadre de ce travail, j'essaie d'utiliser la spécification NgModule et de migrer toutes les parties de mon application vers des modules. Dans l’ensemble, cela s’est très bien passé, à l’exception d’un problème de routage.

"@angular/common": "2.0.0-rc.5",
"@angular/compiler": "2.0.0-rc.5",
"@angular/core": "2.0.0-rc.5",
"@angular/forms": "0.3.0",
"@angular/http": "2.0.0-rc.5",
"@angular/platform-browser": "2.0.0-rc.5",
"@angular/platform-browser-dynamic": "2.0.0-rc.5",
"@angular/router": "3.0.0-rc.1",

Mon application est conçue comme une composition de modules, plusieurs modules étant collés ensemble en tant qu'enfants d'un module parent. Par exemple, j'ai un module d'administration composé d'un module de notifications, d'un module d'utilisateurs et d'un module de téléphonie (par exemple). Les routes vers ces modules devraient ressembler à ...

/admin/notifications/my-notifications
/admin/users/new-user
/admin/telephony/whatever

Dans la version précédente du routeur, cela était facile à réaliser avec des "enfants"

export const AdminRoutes: RouterConfig = [
   {
      path: "Admin",
      component: AdminComponent,
      Children: [
         ...UserRoutes,
         ...TelephonyRoutes,
         ...NotificationRoutes
      ]
   }
]

Dans un autre fichier, faisant partie des sous-modules, je définirais également les routes de module individuelles, c.-à-d.

export const UserRoutes: RouterConfig = [
    {
       path: "users",
       component: userComponent,
       children: [
           {path: "new-user", component: newUserComponent}
       ]
    }

]

Tout cela a très bien fonctionné. Au cours du processus de mise à niveau vers les modules, j'ai tout déplacé dans leurs propres fichiers de routage. Ces deux éléments ressemblent davantage à ceci

const AdminRoutes: Routes = [
    {path: "admin", component: AdminComponent}
] 

export const adminRouting = RouterModule.forChild(AdminRoutes)

et

const UserRoutes: Routes = [
       path: "users",
       component: userComponent,
       children: [
           {path: "new-user", component: newUserComponent}
       ]
] 

export const userRouting = RouterModule.forChild(UserRoutes)

Avec tout cela en place, j'ai un UserModule qui importe le userRouting, puis un AdminModule qui importe les adminRoutes et le UsersModule. Mon idée était que, dans la mesure où UsersModule est un enfant d'AdminModule, le routage fonctionnerait comme auparavant. Malheureusement, ce n'est pas le cas alors je me retrouve avec un itinéraire d'utilisateurs qui est juste

/users/new-user 

au lieu de

/admin/users/new-user

De plus, à cause de cela, le composant nouvel utilisateur n'est pas chargé dans la sortie routeur de mon composant administrateur, ce qui gâche le style et la navigation de mon application.

Je ne peux pas pour la vie de trouver comment référencer les itinéraires de mon UserModule en tant qu'enfants de mon AdminModule. J'ai essayé de le faire à l'ancienne et j'ai des erreurs sur les routes dans deux modules. De toute évidence, depuis la publication de cette nouvelle version, la documentation relative à certains de ces cas est un peu limitée.

Toute aide que n'importe qui peut fournir serait grandement appréciée!

61
John Ackerman

Ok, après avoir bidouillé ça pendant la plus grande partie du week-end, je l'ai fait courir de mon côté. Ce qui a bien fonctionné pour moi à la fin a été de faire ce qui suit:

  • Exportez tout Routes pour chaque module que vous voulez router. N'importez aucun des RouterModule.forChild() dans les modules enfants.
  • Exportez chaque composant visible à partir des définitions de route childs dans la définition de module childs.
  • Importez (c'est-à-dire le mot-clé TypeScript import) tous les itinéraires enfants comme d'habitude et utilisez l'opérateur ... pour les incorporer sous le chemin correct. Je ne pouvais pas le faire fonctionner avec le module enfant définissant le chemin, mais l'avoir sur le parent fonctionne bien (et est compatible avec le chargement paresseux).

Dans mon cas, j'avais trois niveaux dans une hiérarchie comme celle-ci:

  • Racine (/)
    • Éditeur (editor/:projectId)
      • Requête (query/:queryId)
      • Page (page/:pageId)
    • Avant (about)

Les définitions suivantes fonctionnent pour moi pour le chemin /editor/:projectId/query/:queryId:

// app.routes.ts
import {editorRoutes}                   from './editor/editor.routes'

// Relevant excerpt how to load those routes, notice that the "editor/:projectId"
// part is defined on the parent
{
    path: '',
    children: [
        {
            path: 'editor/:projectId',
            children: [...editorRoutes]
            //loadChildren: '/app/editor/editor.module'
        },
    ]
}

Les routes de l'éditeur ressemblent à ceci:

// app/editor/editor.routes.ts
import {queryEditorRoutes}              from './query/query-editor.routes'
import {pageEditorRoutes}               from './page/page-editor.routes'

{
    path: "", // Path is defined in parent
    component : EditorComponent,
    children : [
        {
            path: 'query',
            children: [...queryEditorRoutes]
            //loadChildren: '/app/editor/query/query-editor.module'
        },
        {
            path: 'page',
            children: [...pageEditorRoutes]
            //loadChildren: '/app/editor/page/page-editor.module'
        }
    ]
}

Et la dernière partie de QueryEditor ressemble à ceci:

// app/editor/query/query-editor.routes.ts
{
    path: "",
    component : QueryEditorHostComponent,
    children : [
        { path: 'create', component : QueryCreateComponent },
        { path: ':queryId', component : QueryEditorComponent }
    ]
}

Cependant, pour que cela fonctionne, le Editor général doit importer et exporter le QueryEditor et le QueryEditor doit exporter QueryCreateComponent et QueryEditorComponent car ils sont visibles lors de l'importation. Si vous ne le faites pas, vous obtiendrez des erreurs dans le sens de Component XYZ is defined in multiple modules.

Notez que le chargement différé fonctionne également bien avec cette configuration. Dans ce cas, les itinéraires enfants ne doivent bien sûr pas être importés.

51
Marcus Riemer

J'ai eu le même problème.

La réponse ici est plutôt bonne avec loadChildren:

          {
             path: 'mypath',
             loadChildren : () => myModule
          }

https://github.com/angular/angular/issues/10958

33
Jklf

Je pense que 2.0.0 rc5 n'est pas intéressé maintenant. Mais est est travaillé dans Angular 4, peut être trop tôt aussi.

@NgModule({
    imports: [
        RouterModule.forRoot([
                {path: "", redirectTo: "test-sample", pathMatch: "full"},
                {
                    path: "test-sample",
                    loadChildren: () => TestSampleModule
                }
        ])
    ],
    exports: [RouterModule],
    declarations: [] 
}) 
export class AppRoutingModule{}

@NgModule({
    imports: [
        RouterModule.forChild([{
                    path: "",
                    component: TestSampleComponent,
                    children: [
                        {path: "", redirectTo: "home", pathMatch: "full"},
                        {
                            path: "home",
                            loadChildren: () => HomeModule
                        },
                        {
                            path: "about",
                            loadChildren: () => AboutModule
                        }
                    ]
                }
        ])
    ],
    exports: [RouterModule],
    declarations: []
})
export class TestSampleRoutingModule {}

@NgModule({
    imports: [RouterModule.forChild([{
                    path: "",
                    component: AboutComponent
                }
    ])],
    exports: [RouterModule]
})
export class AboutRoutingModule {}

Prendre en compte sur loadChildren: () => {...}. Ce n'est pas le chargement paresseux.

Voir pour plus de détails feat: Support NgModules hierarchy sans Lazy Loading

2
Leonid Gorshkov

J'ai trouvé un moyen de résoudre cela aussi. En gros, je définis mes itinéraires comme avant, mais cette fois-ci au plus haut niveau. Par exemple ma route d'admin:

const AdminRoutes: Routes = [
   {
      path: 'admin',
      component: AdminComponent,
      children: [
          ...setupRoutes
      ]
   }
]

export const adminRouting = RouterModule.forChild(AdminRoutes)

Mon fichier d'itinéraires de configuration est importé d'une sous-zone, qui définit ses propres itinéraires, y compris plusieurs enfants. Le problème est que ce fichier exporte l'objet "Routes" et non le résultat RouterModule.forChild.

Une fois cette configuration terminée, j'ai supprimé les routes enfant et sous-enfant des définitions de sous-module. J'ai ensuite dû exporter tous les composants utilisés dans les itinéraires, de chacun des sous-modules, comme Marcus mentionné ci-dessus. Une fois que j'ai fait cela, les itinéraires ont commencé à fonctionner exactement comme je le voulais.

Je ne pense pas que j'aime vraiment cette solution car mon module parent en sait trop sur les itinéraires du module enfant. Mais au moins, c’est un moyen facile de contourner le problème pour la RC5, qui ne laisse pas fuir tous mes composants partout. Je vais y aller et marquer la réponse de Marcus comme étant la réponse puisqu'il m'a mis sur la bonne voie.

1
John Ackerman

Utilisez loadChildren. C'est bon. Mais lorsque vous redémarrez le processus de génération, vous obtenez l'erreur de génération. Vous devez faire correspondre le symbole exporté au symbole loadChildren.

import { ChildModule } from './child/child.module';

export function childRouteFactory() {
  return ChildModule;
}

...
  {
    path: 'child',
    loadChildren: childRouteFactory
  }
...
0
Jehong Ahn

Je pense que ma solution est beaucoup plus élégante. À moins que vous ayez réellement besoin de rendre tous les composants parents dans la hiérarchie.

La clé de la compréhension de mon approche réside dans le fait que tous les itinéraires, quel que soit leur degré d'imbrication dans les modules, sont ajoutés au module racine. Exemple rapide, supposons que nous ayons un DepartmentModule et un EmployeeModule auquel nous aimerions accéder en utilisant cette URL

/department/1/employee/2

à quel point nous verrions les détails de l'employé 2. Configuration des itinéraires pour department dans department.routing.ts et employee dans employee.routing.ts fonctionnera pas comme nous le pensions et vous remarquerez que vous pouvez accéder à

/employee/2

de la composante racine, tandis que

/department/1/employee/2

va planter (route non trouvée). Une configuration de route typique dans ce scénario ressemblerait à ceci:

export const departmentRoutes: Routes = [
    { path: 'department', component: DepartmentComponent, children: [
        { path: '', component: DepartmentsComponent },
        { path: ':id', component: DepartmentDetailsComponent }
    ]}
];

export const employeeRoutes: Routes = [
    { path: 'employee', component: EmployeeComponent, children: [
        { path: '', component: EmployeesComponent },
        { path: ':id', component: EmployeeDetailsComponent }
    ]}
];

et EmployeeModule serait importé par DepartmentModule. Maintenant, comme je l'ai dit, cela ne fonctionne malheureusement pas.

Cependant, avec un seul changement, cela va:

export const employeeRoutes: Routes = [
    { path: 'department/:id/employee', component: EmployeeComponent, children: [
        { path: '', component: EmployeesComponent },
        { path: ':id', component: EmployeeDetailsComponent }
    ]}
];

Le problème est que DepartmentModule ne prend plus de partie active dès que vous accédez à une URL employee, mais vous pouvez toujours accéder à tous les paramètres de la ActivatedRoute:

export class EmployeeDetailsComponent {
    departmentId: number;
    employeeId: number;
    constructor(route: ActivatedRoute) {
        route.parent.params.subscribe(params =>
            this.departmentId= +params['id'])
        route.params.subscribe(params => 
            this.employeeId= +params['id']);
    }
}

Je me demande si cela est censé être l'approche officielle, mais pour l'instant cela fonctionne pour moi jusqu'au prochain changement radical de l'équipe Angular 2.

0
Thorsten Westheider

Lorsque vous utilisez ngModules et RC5, la configuration du routage de votre module parent n'a pas besoin de connaître le routage des modules enfants. Vous devez seulement définir les itinéraires pour votre module parent ici. De plus, vous devez importer le module enfant dans votre module parent. Dans le module enfant, vous devez définir vos itinéraires de la manière suivante:

export const childRoutes: Routes = [
  {
    path: 'someChild',
      component: SomeChildComponent,
      children: [
        { path: '', component: SomeChildSubComponent1 },
        { path: 'comp1', component: SomeChildSubComponent1 },
        { path: 'comp2', component: SomeChildSubComponent2 }
      ]
  }
];

Cela vous laissera une url comme/someParent/someChild/comp1 - et les composants sont affichés dans leur routeur-sortie correspondant. Remarque: vous DEVEZ décoller un composant pour le chemin vide. Sinon, vous ne pouvez pas accéder à vos enfants.

0
westor