我有一个 generic
CanActivate
守卫,想改变相应路径段(被测试的那个)的矩阵参数。
使用给定的路由配置:
{
path: 'parent',
canActivate: [ensureSortMatrixParamsGuard],
children: [
{ path: 'child', component: ChildComponent },
]
}
还有
CanActivate
守卫:
const ensureSortMatrixParamsGuard: CanActivateFn = (route, state) => {
if (!route.paramMap.has('sort')) {
const tree = // TODO -> should be `/parent;sort=asc
return tree
}
return true
}
如何在守卫中创建一个
UrlTree
以确保相应路径段(添加守卫的那个,=sort
)上的/parent
矩阵参数?
使用以下解决方案,我可以以某种方式访问 url 段,但只能以一种非常 hacky 的方式 - 相应的段并不总是最后一个(仅当直接访问路由时,而不是其子项之一)。
const tree = inject(Router).parseUrl(state.url)
const primarySegments = tree.root.children[PRIMARY_OUTLET].segments
const lastPrimarySegment = primarySegments[primarySegments.length - 1]
// then add the matrix param and then return the `tree`
lastPrimarySegment.parameters['sort'] = 'asc'
我怎么能断定
UrlTree
的哪一段对应于RouteSnapshot
?
更新
我找到了一个似乎有效的方法:
/**
* returns all the parent ARS and provided ARS in an array
* e.g. [ars.parent.parent, ars.parent, ars]
*/
function anchestorRouteSnapshots(ars: ActivatedRouteSnapshot | null): ActivatedRouteSnapshot[] {
return ars ? [...anchestorRouteSnapshots(ars.parent), ars] : []
}
const anchestors = anchestorRouteSnapshots(route)
.map((ars) => ars.routeConfig)
.filter((rc) => !!rc && rc.path !== '') // there might be ARS without routeConfig (e.g. AppRoot) or with an empty path part
// this is the segment from the UrlTree I want to add the matrix param
const segment = primarySegments[anchestors.length - 1]
我创建了一个Stackblitz:)
你能试试这段代码并让我知道错误吗?
{ Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, CanActivateFn, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class EnsureSortMatrixParamsGuard implements CanActivate, CanActivateChild {
constructor(private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
return this.ensureSortMatrixParams(route, state);
}
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
return this.ensureSortMatrixParams(childRoute, state);
}
private ensureSortMatrixParams(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
if (!route.paramMap.has('sort')) {
const segments = this.getSegmentsForRoute(route, state);
segments[segments.length - 1].parameters['sort'] = 'asc';
const tree = this.router.createUrlTree([], { queryParamsHandling: 'preserve', fragment: state.fragment, queryParams: state.queryParams });
tree.root.children[0].segments = segments;
return tree;
}
return true;
}
private getSegmentsForRoute(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): any[] {
const url = this.router.createUrlTree([], {relativeTo: route, queryParamsHandling: 'merge'}).toString();
const tree = this.router.parseUrl(url);
const primarySegments = tree.root.children[0].segments;
const ancestorRouteSnapshots = this.getAncestorRouteSnapshots(route);
const matchingSegmentIndex = this.getMatchingSegmentIndex(primarySegments, ancestorRouteSnapshots);
return primarySegments.slice(0, matchingSegmentIndex + 1);
}
private getAncestorRouteSnapshots(route: ActivatedRouteSnapshot): ActivatedRouteSnapshot[] {
const snapshots = [];
while (route.parent) {
snapshots.push(route.parent);
route = route.parent;
}
return snapshots.reverse();
}
private getMatchingSegmentIndex(primarySegments: any[], ancestorRouteSnapshots: ActivatedRouteSnapshot[]): number {
let matchingSegmentIndex = -1;
for (let i = 0; i < ancestorRouteSnapshots.length; i++) {
const routeConfig = ancestorRouteSnapshots[i].routeConfig;
if (routeConfig && routeConfig.path) {
const matchingSegment = primarySegments.find(segment => segment.path === routeConfig.path);
if (matchingSegment) {
matchingSegmentIndex = primarySegments.indexOf(matchingSegment);
}
}
}
return matchingSegmentIndex;
}
}