Angular 中的嵌套路由
Nested Routing in Angular
这可能是一个常见问题,如果有更好的答案,请指点我。话虽这么说,但问题是:
在顶层,我正在开发的 angular 应用程序公开了一个登录路径和 4 个独立仪表板的路径,具体取决于谁登录。这部分按预期工作。
对于每个仪表板,我都有一个或多或少相同的侧面导航(某些选项仅针对特定类型的用户显示)。在仪表板中,我有一个嵌套的路由器插座。每当我尝试在嵌套插座内加载模块时,Angular 无法匹配路径。这是我目前拥有的:
应用-routing.module.ts
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'login' },
{ path: 'login', loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule) },
//{ path: 'dashboard', loadChildren: () => import('./modules/dashboard/dashboard.module').then(m => m.DashboardModule) }
{ path: 'admin', loadChildren: () => import('./modules/admin/admin.module').then(m => m.AdminModule) },
];
admin-routing.module.ts
const routes: Routes = [
{ path: '', pathMatch: 'full', component: AdminComponent, children: [
{ path: 'employee', loadChildren: () => import('./../employee/employee.module').then(m => m.EmployeeModule) },
{ path: 'attendance', loadChildren: () => import('./../attendance/attendance.module').then(m => m.AttendanceModule) },
{ path: 'customer', loadChildren: () => import('./../customer/customer.module').then(m => m.CustomerModule) },
{ path: 'order', loadChildren: () => import('./../order/order.module').then(m => m.OrderModule) },
{ path: 'expense', loadChildren: () => import('./../expense/expense.module').then(m => m.ExpenseModule) },
]},
];
app.component.html
<router-outlet></router-outlet>
admin.component.html
<mat-drawer-container mode="side" id="dashboard-drawer-container" hasBackdrop="false">
<mat-drawer #drawer id="sidenav-container">
<app-sidenav></app-sidenav>
</mat-drawer>
<div id="dashboard-container">
<router-outlet></router-outlet>
</div>
</mat-drawer-container>
现在预期的行为如下:
- 导航到 /admin 时,将呈现 AdminComponent 并且 sidenav 将可见
- 当点击sidenav上的link时,内容应该在管理组件的嵌套路由器中呈现(例如,admin/employee)
- 当在(2)中加载的模块内部访问其他路由时,它应该在该模块的出口内呈现(例如,admin/employee/:id),用于员工详细信息页面,其中,员工模块有一个嵌套路由器
我尝试使用命名插座,但它一直在抛出错误。如果我将子项从管理路由中移出并使它们成为独立的路由,它会起作用,但是内容会呈现在最外面的(应用程序)路由器插座上,而不会呈现 sidenav。
如有任何帮助或建议,我们将不胜感激。
如果你需要为不同的模块使用不同的 UI 你可以通过在 app-routing.module.ts.
中定义父组件来实现
app-routing.module.ts.
const routes: Routes = [
{
path: '',
component: AdminComponent, //you can set different UI like navigation/header
children: [
{ path: '',redirectTo: 'dashboard',pathMatch: 'full'}, //set default redirect
{ path: 'dashboard', loadChildren: () =>import('./../dashboard/dashboard.module').then(m => m.DashboardModule) },
{ path: 'employee', loadChildren: () => import('./../employee/employee.module').then(m => m.EmployeeModule) },
{ path: 'attendance', loadChildren: () => import('./../attendance/attendance.module').then(m => m.AttendanceModule) },
{ path: 'customer', loadChildren: () => import('./../customer/customer.module').then(m => m.CustomerModule) },
{ path: 'order', loadChildren: () => import('./../order/order.module').then(m => m.OrderModule) },
{ path: 'expense', loadChildren: () => import('./../expense/expense.module').then(m => m.ExpenseModule) },
]
},
{
path: '',
component: AuthComponent, //different UI for child components, without navigation
children: [
{ path: 'maintenance',loadChildren: () => import('./../maintenance/maintenance.module').then(m => m.MaintenanceModule) },
{ path: 'login',loadChildren () => import('./../authentication/authentication.module').then(m => m.AuthenticationModule) },
{ path: 'landing',loadChildren: () => import('./../landing/landing.module').then(m => m.LandingModule) }
]
}
您的子模块例如:admin/employee/:id 将呈现父模块的 UI(例如:AdminComponent),
让我们把问题分解成一个小问题:
app.module.ts
const routes: Routes = [
{
path: '',
// pathMatch: 'full',
children: [
{
path: 'foo',
component: FooComponent
},
],
},
{
path: '**',
component: NotFoundComponent,
}
];
app.component.html
<router-outlet></router-outlet>
<button routerLink="/foo">go to foo</button>
如果我们点击按钮,Angular 路由器将安排一个路由转换。这涉及到一个非常有趣的过程,它由多个阶段.
组成
其中一个阶段称为 Apply Redirects,它是解决重定向的地方,也是 NoMatch
错误的来源。这也是我们可以找到更多关于 pathMatch: 'full'
.
的地方
在此阶段,它将遍历每个 配置对象 并尝试找到第一个与已发布的 url(例如 /foo
)匹配的对象。
首先会遇到这个(发生在matchSegmentAgainstRoute
):
{
path: '',
// pathMatch: 'full',
children: [
{
path: 'foo',
component: FooComponent
},
],
},
然后,match
函数将被调用:
const {matched, consumedSegments, lastChild} = match(rawSegmentGroup, route, segments);
这里,我们停在 route.path === ''
:
if (route.path === '') {
if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) {
return {matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
}
return {matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
}
因此,这是 pathMatch
选项产生影响的一种情况。使用当前配置(pathMatch
未设置),
return {matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
将到达,然后它将继续遍历 children
数组。所以,在这种情况下,FooComponent
将成功显示。
但是,如果我们有 pathMatch: 'full'
,那么表达式
if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) { }
将是 true
,因为 segments.length > 0
,在这种情况下段是 ['foo']
。所以,我们会得到 matched: false
,这意味着 FooComponent
不会出现在视图中。
这可能是一个常见问题,如果有更好的答案,请指点我。话虽这么说,但问题是:
在顶层,我正在开发的 angular 应用程序公开了一个登录路径和 4 个独立仪表板的路径,具体取决于谁登录。这部分按预期工作。
对于每个仪表板,我都有一个或多或少相同的侧面导航(某些选项仅针对特定类型的用户显示)。在仪表板中,我有一个嵌套的路由器插座。每当我尝试在嵌套插座内加载模块时,Angular 无法匹配路径。这是我目前拥有的:
应用-routing.module.ts
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'login' },
{ path: 'login', loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule) },
//{ path: 'dashboard', loadChildren: () => import('./modules/dashboard/dashboard.module').then(m => m.DashboardModule) }
{ path: 'admin', loadChildren: () => import('./modules/admin/admin.module').then(m => m.AdminModule) },
];
admin-routing.module.ts
const routes: Routes = [
{ path: '', pathMatch: 'full', component: AdminComponent, children: [
{ path: 'employee', loadChildren: () => import('./../employee/employee.module').then(m => m.EmployeeModule) },
{ path: 'attendance', loadChildren: () => import('./../attendance/attendance.module').then(m => m.AttendanceModule) },
{ path: 'customer', loadChildren: () => import('./../customer/customer.module').then(m => m.CustomerModule) },
{ path: 'order', loadChildren: () => import('./../order/order.module').then(m => m.OrderModule) },
{ path: 'expense', loadChildren: () => import('./../expense/expense.module').then(m => m.ExpenseModule) },
]},
];
app.component.html
<router-outlet></router-outlet>
admin.component.html
<mat-drawer-container mode="side" id="dashboard-drawer-container" hasBackdrop="false">
<mat-drawer #drawer id="sidenav-container">
<app-sidenav></app-sidenav>
</mat-drawer>
<div id="dashboard-container">
<router-outlet></router-outlet>
</div>
</mat-drawer-container>
现在预期的行为如下:
- 导航到 /admin 时,将呈现 AdminComponent 并且 sidenav 将可见
- 当点击sidenav上的link时,内容应该在管理组件的嵌套路由器中呈现(例如,admin/employee)
- 当在(2)中加载的模块内部访问其他路由时,它应该在该模块的出口内呈现(例如,admin/employee/:id),用于员工详细信息页面,其中,员工模块有一个嵌套路由器
我尝试使用命名插座,但它一直在抛出错误。如果我将子项从管理路由中移出并使它们成为独立的路由,它会起作用,但是内容会呈现在最外面的(应用程序)路由器插座上,而不会呈现 sidenav。
如有任何帮助或建议,我们将不胜感激。
如果你需要为不同的模块使用不同的 UI 你可以通过在 app-routing.module.ts.
中定义父组件来实现app-routing.module.ts.
const routes: Routes = [
{
path: '',
component: AdminComponent, //you can set different UI like navigation/header
children: [
{ path: '',redirectTo: 'dashboard',pathMatch: 'full'}, //set default redirect
{ path: 'dashboard', loadChildren: () =>import('./../dashboard/dashboard.module').then(m => m.DashboardModule) },
{ path: 'employee', loadChildren: () => import('./../employee/employee.module').then(m => m.EmployeeModule) },
{ path: 'attendance', loadChildren: () => import('./../attendance/attendance.module').then(m => m.AttendanceModule) },
{ path: 'customer', loadChildren: () => import('./../customer/customer.module').then(m => m.CustomerModule) },
{ path: 'order', loadChildren: () => import('./../order/order.module').then(m => m.OrderModule) },
{ path: 'expense', loadChildren: () => import('./../expense/expense.module').then(m => m.ExpenseModule) },
]
},
{
path: '',
component: AuthComponent, //different UI for child components, without navigation
children: [
{ path: 'maintenance',loadChildren: () => import('./../maintenance/maintenance.module').then(m => m.MaintenanceModule) },
{ path: 'login',loadChildren () => import('./../authentication/authentication.module').then(m => m.AuthenticationModule) },
{ path: 'landing',loadChildren: () => import('./../landing/landing.module').then(m => m.LandingModule) }
]
}
您的子模块例如:admin/employee/:id 将呈现父模块的 UI(例如:AdminComponent),
让我们把问题分解成一个小问题:
app.module.ts
const routes: Routes = [
{
path: '',
// pathMatch: 'full',
children: [
{
path: 'foo',
component: FooComponent
},
],
},
{
path: '**',
component: NotFoundComponent,
}
];
app.component.html
<router-outlet></router-outlet>
<button routerLink="/foo">go to foo</button>
如果我们点击按钮,Angular 路由器将安排一个路由转换。这涉及到一个非常有趣的过程,它由多个阶段.
组成其中一个阶段称为 Apply Redirects,它是解决重定向的地方,也是 NoMatch
错误的来源。这也是我们可以找到更多关于 pathMatch: 'full'
.
的地方
在此阶段,它将遍历每个 配置对象 并尝试找到第一个与已发布的 url(例如 /foo
)匹配的对象。
首先会遇到这个(发生在matchSegmentAgainstRoute
):
{
path: '',
// pathMatch: 'full',
children: [
{
path: 'foo',
component: FooComponent
},
],
},
然后,match
函数将被调用:
const {matched, consumedSegments, lastChild} = match(rawSegmentGroup, route, segments);
这里,我们停在 route.path === ''
:
if (route.path === '') {
if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) {
return {matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
}
return {matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
}
因此,这是 pathMatch
选项产生影响的一种情况。使用当前配置(pathMatch
未设置),
return {matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
将到达,然后它将继续遍历 children
数组。所以,在这种情况下,FooComponent
将成功显示。
但是,如果我们有 pathMatch: 'full'
,那么表达式
if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) { }
将是 true
,因为 segments.length > 0
,在这种情况下段是 ['foo']
。所以,我们会得到 matched: false
,这意味着 FooComponent
不会出现在视图中。