55

Jestem w trakcie ulepszania aplikacji, nad którą pracuję, na najnowszego kandydata do wydania Angular 2. W ramach tej pracy próbuję korzystać ze specyfikacji NgModule i migrować wszystkie części mojej aplikacji do modułów. W większości wypadków wszystko poszło bardzo dobrze, z wyjątkiem problemu z routingiem.Jak przejść do modułu jako dziecko modułu - Angular 2 RC 5

"@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", 

Moja aplikacja jest zbudowana jako kompozycja modułów, z kilkoma modułami sklejanymi jako elementy potomne modułu nadrzędnego. Na przykład mam moduł administracyjny, który składa się z modułu powiadomień, modułu użytkowników i modułu telefonicznego (na przykład). Trasy do tych modułów powinno wyglądać ...

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

We wcześniejszym wydaniu routera, to było łatwe do osiągnięcia za pomocą „dzieci”

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

w innym pliku, jako część podmoduły, zdefiniowałbym również poszczególne trasy modułów, tj.

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

] 

Wszystko działało bardzo dobrze. W procesie aktualizacji do modułów, przeniosłem wszystko do własnych indywidualnych plików routingu zamiast więc teraz te dwa wyglądają bardziej jak ten

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

export const adminRouting = RouterModule.forChild(AdminRoutes) 

i

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

export const userRouting = RouterModule.forChild(UserRoutes) 

ze wszystkimi, że w miejscu, muszę moduł Users, który importuje userRouting, a następnie moduł administracyjny, który importuje adminRoutes i moduł UsersModule. Myślałem, że ponieważ UsersModule jest dzieckiem AdminModule, routing działałby tak jak kiedyś. Niestety, nie ma więc skończyć z trasą użytkowników, że jest po prostu

/users/new-user 

zamiast

/admin/users/new-user 

Ponadto, z tego powodu, komponent nowy użytkownik nie jest ładowany do wylot routera z mojego elementu administratora, który odrzuca styl i nawigację mojej aplikacji.

Nie mogę na całe życie wymyślić, jak odwołać się do tras mojego modułu użytkownika jako dzieci mojego AdminModule. Próbowałem zrobić to w starym stylu i uzyskać błędy dotyczące tras znajdujących się w dwóch modułach. Oczywiście, ponieważ jest to nowe wydanie, dokumentacja dotycząca niektórych z tych przypadków jest nieco ograniczona.

Każda pomoc, którą każdy może zapewnić, byłaby bardzo ceniona!

+1

Istnieje własność 'loadChildren' na trasach, która pozwala wyraźnie wymienić moduł. Dokumenty mówią o tym tylko w kontekście leniwego ładowania ... –

+0

Myślę, że to jest dokument, który powinieneś odnosić się do https://angular.io/guide/ngmodule#lazy-loading-modules-with-the -router – jitenagarwal19

+0

Znalazłem bardzo przydatny [ten artykuł] (https://toddmotto.com/right-component-router) Todda Motto na temat routera Angular. 'loadChildren' również jest bardzo dobrze wyjaśniony. – Hinrich

Odpowiedz

38

Dobra, po tym, jak bawiłem się z tym przez większą część weekendu, uruchomiłem to na moim końcu. Co pracował dla mnie w końcu było wykonać następujące czynności:

  • wyeksportować wszystkie Routes dla każdego modułu, który chcesz przekierować. Nie importuj żadnego z RouterModule.forChild() w modułach podrzędnych.
  • Eksportuj co element, który jest widoczny z definicji tras dziecka w definicji modułu Childs.
  • Importuj (co oznacza słowo kluczowe import) wszystkich ścieżek podrzędnych w zwykły sposób i użyj operatora ..., aby uwzględnić je pod prawidłową ścieżką. Nie mogłem go uruchomić z modułem-dzieckiem definiującym ścieżkę, ale posiadanie go na rodzica działa dobrze (i jest kompatybilne z leniwym ładowaniem).

W moim przypadku miałem trzy poziomy w hierarchii tak:

  • root (/)
    • Editor (editor/:projectId)
      • Query (query/:queryId)
      • Strona (page/:pageId)
    • przednia (about)

następujące definicje dla mnie za drogi /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' 
     }, 
    ] 
} 

trasy edytorze wyglądać następująco:

// 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' 
     } 
    ] 
} 

I ostatnia część fo R QueryEditor wygląda następująco:

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

Jednak do tej pracy, ogólne Editor potrzeby importowania i wyeksportować i QueryEditor potrzeb QueryEditor eksportować QueryCreateComponent i QueryEditorComponent jak te widoczne są z importu. W przeciwnym razie wystąpią błędy zgodne z Component XYZ is defined in multiple modules.

Zauważ, że leniwy ładunek również działa dobrze z tą konfiguracją, w takim przypadku trasy potomne nie powinny być oczywiście importowane.

+0

To wygląda obiecująco, a ja doceniam czas, jaki w nim wkładasz. Czy możesz trochę wyjaśnić definicje modułów i co dokładnie eksportujesz z plików routingu? Czy eksportujesz trasy [] lub eksportujesz RouterModule.forChild() z plików routingu? A gdzie eksportujesz/importujesz te moduły, aby były widoczne? Dotychczasowe próby doprowadziły do ​​błędu w dwukrotnym zdefiniowaniu RouterOutlet, a następnie problemu, który próbuje wyeksportować Routes [] w module, gdy eksportowany jest nieoczekiwany [obiekt, obiekt]. –

+2

Och, przepraszam ... jakoś przeczytałem twój komentarz, a potem nie chciałem odpowiadać. Możesz zobaczyć całą konfigurację, którą znalazłem na https://bitbucket.org/marcusriemer/esqulino/src/e1338eea74e42f0468f28554cd6a150f9295350a/client/app/?at=master –

+0

Spędziłem trochę czasu, aby uzyskać tę odpowiedź. Dziękuję za wyjaśnienia i szczegółową odpowiedź. Masz szansę uzyskać link do kanciastej dokumentacji? –

1

Znalazłem sposób na rozwiązanie tego problemu. Zasadniczo definiuję swoje trasy tak jak kiedyś, ale tym razem na najwyższym poziomie. Na przykład mojej trasie admin:

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

export const adminRouting = RouterModule.forChild(AdminRoutes) 

Moja konfiguracja trasy plik jest importowany z sub obszaru, który wyznacza trasy na jego własny, w tym więcej dzieci. Połów jest taki, że ten plik eksportuje obiekt "Trasy", a nie wynik RouterModule.forChild.

Po tym ustawieniu usunąłem ścieżki podrzędne i potomne z definicji modułów. Następnie musiałem wyeksportować wszystkie komponenty używane na trasach z każdego z submodułów, tak jak wspomniano powyżej. Kiedy to zrobiłem, trasy zaczęły działać tak, jak chciałem.

Nie sądzę, że podoba mi się to rozwiązanie, ponieważ mój moduł nadrzędny wie zbyt wiele o trasach modułów potomnych. Ale przynajmniej jest to łatwy sposób obejścia go dla RC5 i nie powoduje wycieku wszystkich moich komponentów w każdym miejscu. Pójdę naprzód i zaznaczę odpowiedź Marcusa jako odpowiedź, od kiedy postawił mnie na właściwej drodze.

+0

to działa dla mnie, dzięki! –

+0

Cześć John, twoje podejście działa, jak jednak radzić sobie z leniwym ładowaniem modułu? Wydaje się, że przy takim podejściu nie można leniwej załadować innych modułów. –

0

Podczas korzystania z modułów ngModules i RC5, konfiguracja routingu modułu macierzystego nie musi nic wiedzieć o routingu modułów podrzędnych. Tutaj musisz tylko zdefiniować trasy dla modułu macierzystego. Ponadto należy zaimportować moduł potomny do modułu macierzystego. W module dziecka trzeba określić swoje trasy w ten sposób:

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

To pozwoli masz url podobny/someParent/someChild/Comp1 - a składniki są wyświetlane w ich odpowiednim router gniazdka. Uwaga: MASZ WYMIEŃ część na pustą ścieżkę. W przeciwnym razie nie będziesz w stanie nawigować do swoich dzieci.

+1

Działa to tylko dla pierwszego poziomu zagnieżdżania. Dla wszystkich kolejnych poziomów trasy są dodawane do modułu głównego i niedostępne przez ich rodzica. –

+0

High Torsten, niestety masz rację. Możesz osiągnąć swój komponent, modyfikując informacje o ścieżce obiektu trasy, poprzedzając ścieżkę rodziców. Ale nadal nie zobaczysz go we właściwym gniazdku routera. To wydaje się być również problemem z twoim rozwiązaniem. Może to jest błąd? Czy wiesz, jeśli jest już otwarty problem? – westor

+0

Myślę, że jest to zgodne z projektem, można obejść go przy użyciu leniwego ładowania i to prawdopodobnie i tak lepsze rozwiązanie. –

1

Mam to również do pracy i chyba, że ​​rzeczywiście musisz renderować wszystkie elementy macierzyste w hierarchii, myślę, że moje rozwiązanie jest znacznie bardziej eleganckie.

Kluczem do zrozumienia mojego podejścia jest to, że wszystkie trasy, bez względu na to, jak głęboko zagnieżdżone w modułach są dodawane do modułu głównego. Krótki przykład, powiedzmy, że mamy DepartmentModule oraz EmployeeModule które chcielibyśmy, aby poruszać się za pomocą tego adresu

/department/1/employee/2 

w tym momencie chcielibyśmy widzieć pracownik 2 w szczegóły. Konfiguracja trasy dla department w department.routing.ts i employee w employee.routing.ts będzie nie praca nasz sposób zamierzony i zauważysz, że można przejść do

/employee/2 

od składnika głównego, natomiast

/department/1/employee/2 

będzie crash (nie znaleziono trasy). Typowa konfiguracja trasy w tym scenariuszu będzie wyglądać następująco:

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 } 
    ]} 
]; 

i EmployeeModule byłyby przywożone przez DepartmentModule. Teraz, jak już powiedziałem, to niestety działa.

Jednak z tylko jedną zmianę to będzie:

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

Połów jest, że DepartmentModule nie bierze czynny udział już tak szybko nawigować do employee URL, ale nadal można uzyskać dostęp do każdego parametru z 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']); 
    } 
} 

zastanawiam się, czy to ma być oficjalne podejście, ale na razie to działa na mnie aż do następnej zmiany zerwania z Kątowymi 2 drużyny.

1

myślę 2.0.0 RC5 nie jest zainteresowany teraz. Ale działa w Angular 4, może być również wcześnie.

@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 {} 

Weź pod uwagę na loadChildren:() => {...}. To nie jest leniwe ładowanie.

Zobacz szczegółowe informacje feat: Support NgModules hierarchy sans Lazy Loading

0

użytkowania loadChildren. W porządku. Ale po ponownym uruchomieniu procesu kompilacji pojawi się błąd kompilacji. Powinieneś dopasować wyeksportowany symbol do loadChildren.

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

export function childRouteFactory() { 
    return ChildModule; 
} 

... 
    { 
    path: 'child', 
    loadChildren: childRouteFactory 
    } 
...