简体   繁体   English

如何使用嵌套模块进行路由应该在Angular 2中工作?

[英]How is routing with nested modules supposed to work in Angular 2?

I can't for the love of cookies figure out how to configure the most recent incarnation of the Angular 2 router (the one from the final release). 我不能因为对饼干的热爱而弄清楚如何配置Angular 2路由器的最新版本(最终版本中的那个)。 I have a rather large Angular 2 RC5 app up and running, but need to migrate to the final release now before I can move on. 我有一个相当大的Angular 2 RC5应用程序启动并运行,但在我继续前进之前需要迁移到最终版本。

In this app I have a lot of nested components that, according to Google's docs, should be organised as features in module. 在这个应用程序中,我有很多嵌套组件,根据谷歌的文档,应该组织为模块中的功能。 The typical URL in my app looks like so 我的应用中的典型网址如此

user/34/section/95/client/13/<feature>

but there are also a few others, such as 但也有一些其他的,比如

user/34/settings

so in this case, settings would be a feature module. 所以在这种情况下, settings将是一个功能模块。

I've read the docs, which are rather long-winded and are in no hurry to cut to the chase, only to find out that this case isn't covered at all. 我已经阅读了这些文档,这些文档相当冗长,并不急于切入追逐,只是发现这个案例根本没有涉及。 I can't believe that this scenario isn't going to work, it would seem to be a huge oversight so I suppose the problem is that I haven't quite grasped how this router actually works - and I have a feeling that I'm not the only one. 我无法相信这种情况不会起作用,这似乎是一个巨大的疏忽,所以我想问题是我还没有完全理解这个路由器实际上是如何工作的 - 我感觉我是我不是唯一的一个。

I've created this plunker to illustrate my point. 我已经创建了这个plunker来说明我的观点。

In this plunker I got 2 levels of nesting 在这个plunker我有2级嵌套

app -> benutzer (user) -> klient (client)

just enough to make the issue appear. 足以使问题出现。 The expected outcome is that I can navigate to 预期的结果是我可以导航到

benutzer/78/klient/56

ie the router actually finds that route, which at the moment it doesn't. 即路由器实际上找到了该路由,此时它没有。

Before I moved the code to plunker and added dashboard.component (because I can't easily modify the URL in plunker, so I have to resort to routerLink ) I could actually navigate to 在我将代码移动到plunker并添加了dashboard.component (因为我无法轻松修改plunker中的URL,所以我不得不求助于routerLink )我实际上可以导航到

klient/56

which shouldn't be possible. 这是不可能的。 It seems that each route defined in another module is added to the root instead of building on top of each other. 看起来在另一个模块中定义的每个路由都被添加到根目录而不是彼此重叠。

Any help with this is greatly appreciated. 非常感谢任何帮助。

Look at all your routes. 看看你所有的路线。

appRoutes = [      // AppModule
  { path: '', redirectTo: 'dashaboard' },
  { path: 'dashboard', component: DashboardComponent
];
benutzerRoutes = [ // BenutzerModule
  { path: 'benutzer/:id', component BenutzerComponent }
];
klientRoutes = [   // KlinetModule
  { path: 'klient/:id', component: KlientComponent }
]

The way you import your modules doesn't have any effect on how the routes are constructed. 导入模块的方式对路径的构造方式没有任何影响。 How they are constructed are simply based on how we construct the. 如何构建它们只是基于我们如何构建它。 What you are expecting with this 你对此有何期待?

AppModule
   imports -> BenutzerModule
                   imports -> KlinetModule

leading to the following routes 通往以下路线

dashboard/benutzer/:id/klient/:id

is just not the case. 事实并非如此。 Each one of those route arrays are simply added to the root. 这些路由数组中的每一个都只是添加到根目录中。 That's why you can access klient/:id and not dashboard/benutzer/:id . 这就是为什么你可以访问klient/:id而不是dashboard/benutzer/:id

I've read the complete documentation for routing a few times, and there aren't any example of nested routes in different modules. 我已经阅读了几次完整的路由文档,并且在不同的模块中没有任何嵌套路由的示例。 All the examples had modules that were loaded from the root route, as your example does, or having nested routes being part of the same route configuration. 所有示例都具有从根路由加载的模块,如您的示例所示,或者嵌套路由是相同路由配置的一部分。 So since there are no example I guess we need to just work with what we know and decide for ourselves what's the best way. 所以既然没有例子,我想我们需要与我们所知道的一起工作,并为自己决定什么是最好的方式。

There are a couple ways I can think of. 我有几种方法可以想到。 The first, most obvious but IMO more intrusive than the next option, is to just add the full routes to the paths 第一个,最明显但IMO比下一个选项更具侵入性,只是将完整路径添加到路径中

appRoutes = [      // AppModule
  { path: '', redirectTo: 'dashaboard' },
  { path: 'dashboard', component: DashboardComponent
];
benutzerRoutes = [ // BenutzerModule
  { path: 'dashboard/benutzer/:id', component BenutzerComponent }
];
klientRoutes = [   // KlinetModule
  { path: 'dashboard/benutzer/:id/klient/:id', component: KlientComponent }
]

I say this option is more intrusive, as it forces the children to know about the parent. 我说这个选项更具侵入性,因为它迫使孩子们了解父母。 In Angular 2 architecture that is counter to how we structure our components. 在Angular 2架构中,这与我们构建组件的方式相反。 Parents should know about children, but not necessarily vice versa. 父母应该了解孩子,但不一定相反。

The other option I can think of is to use loadChildren to load the child modules lazily. 我能想到的另一个选择是使用loadChildrenloadChildren地加载子模块。 I say "lazily" because I can't figure out how to do this eagerly, and not sure if it is even possible to do so. 我说“懒惰”,因为我无法弄清楚如何做到这一点,并且不确定是否有可能这样做。 To load the children lazily, we can do 为了让孩子们懒洋洋地加载,我们可以做到

export const appRoutes: Routes = [
  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
  { path: 'dashboard', component: DashboardComponent },
  {
    path: 'dashboard/benutzer',
    loadChildren: 'app/benutzer/benutzer.module#BenutzerModule'
  },
  { path: '**', component: NotFoundComponent }
];
export const benutzerRoutes: Routes = [
  { path: ':id', component: BenutzerComponent },
  {
    path: ':id/klient',
    loadChildren: 'app/klienten/klienten.module#KlientenModule'
  }
];
export const klientenRoutes: Routes = [
  { path: ':id', component: KlientComponent }
];

In this case we would remove the all the child module imports from their parent @NgModule . 在这种情况下,我们将从其父@NgModule删除所有子模块导入。 This allows for lazy loading of the module. 这允许延迟加载模块。 If we leave then, then the module will be loaded eagerly on startup, but not have the desired effect (hence why I said I am not how to do this eagerly). 如果我们离开那么,那么模块将在启动时急切地加载,但没有达到预期的效果(因此我说我不是如何急切地这样做)。

Also notice the loadChildren . 另请注意loadChildren In the above example I use app as the root. 在上面的例子中,我使用app作为根。 The only reason is that I tested in local environment. 唯一的原因是我在当地环境中测试过。 I am not a big fan of Plunker. 我不是普兰克的忠实粉丝。 For your Plunker though, you should use src as the root. 但是对于你的Plunker,你应该使用src作为根。

IMO, the lazy loading looks cleaner, as child doesn't know about parent, but this forces you to lazily load the modules, which may not be desired. IMO,懒惰的加载看起来更干净,因为孩子不知道父母,但这迫使你懒惰加载模块,这可能是不希望的。 In some cases though, it is desired, as it allows for a lighter initial load. 但在某些情况下,需要,因为它允许较轻的初始负载。

For more on lazy loading, see the docs routing section on Asynchronous Routing 有关延迟加载的更多信息,请参阅异步路由上的docs routing部分

Found a possible solution, copying my answer from https://stackoverflow.com/a/39629949/370935 找到了一个可能的解决方案,从https://stackoverflow.com/a/39629949/370935复制我的答案

I got this to work as well and unless you actually need to render all parent components in the hierarchy I think my solution is far more elegant. 我也让它工作,除非你真的需要渲染层次结构中的所有父组件,我认为我的解决方案更优雅。

The key to understanding my approach is that all routes, no matter how deeply nested in modules are added to the root module. 理解我的方法的关键是所有路由,无论嵌套在模块中的深度如何都添加到根模块。 Quick example, let's say we have a DepartmentModule and an EmployeeModule which we'd like to navigate to using this URL 快速举例,假设我们有一个DepartmentModule和一个EmployeeModule ,我们想要导航到这个URL

/department/1/employee/2

at which point we'd see employee 2's details. 在这一点上,我们会看到员工2的详细信息。 Configuring routes for department in department.routing.ts and employee in employee.routing.ts will not work the way we intended and you'll notice that you can navigate to 配置路由的departmentdepartment.routing.tsemployeeemployee.routing.ts无法正常工作,我们希望的方式,你会发现,你可以浏览到

/employee/2

from the root component, while 从根组件,而

/department/1/employee/2

will crash (route not found). 会崩溃(未找到路线)。 A typical route configuration in this scenario would look like this: 此方案中的典型路由配置如下所示:

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

and EmployeeModule would be imported by DepartmentModule . DepartmentModule将导入EmployeeModule Now, like I said, that doesn't work unfortunately. 现在,正如我所说,不幸的是,这不起作用。

However, with just a single change it will: 但是,只需一次更改,它将:

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

The catch is, that DepartmentModule is not taking an active part anymore as soon you navigate to an employee URL, but you still can access every parameter from the ActivatedRoute : 问题是,当您导航到employee URL时, DepartmentModule不再处于活动状态,但您仍然可以从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']);
    }
}

I wonder if this is supposed to be the official approach, but for now this works for me until the next breaking change from the Angular 2 team . 我想知道这是否应该是官方方法,但是现在这对我有用,直到Angular 2团队的下一次突破性改变。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM