Angular 错误:无法重新附加从其他路由创建的ActivatedRouteSnapshot
我正在尝试实现Angular 错误:无法重新附加从其他路由创建的ActivatedRouteSnapshot,angular,typescript,angular2-routing,Angular,Typescript,Angular2 Routing,我正在尝试实现路由使用策略类。当我导航到顶级路径时,它工作正常 一旦路径具有子路径,并且我导航到子路径,然后导航回顶层路径,我将收到以下错误: 错误:未捕获(承诺中):错误:无法重新附加从其他路由创建的ActivatedRouteSnapshot 我创建了一个示例来演示错误。我发现plunker在IE 11中不起作用,请在最新版本的Chrome中查看它 重现错误的步骤: 步骤1: 步骤2 步骤3 步骤4 您可以在控制台中查看错误: 我已经尝试了在这个平台上找到的实现 以及这个stack
路由使用策略
类。当我导航到顶级路径时,它工作正常
一旦路径具有子路径,并且我导航到子路径,然后导航回顶层路径,我将收到以下错误:
错误:未捕获(承诺中):错误:无法重新附加从其他路由创建的ActivatedRouteSnapshot
我创建了一个示例来演示错误。我发现plunker在IE 11中不起作用,请在最新版本的Chrome中查看它
重现错误的步骤:
步骤1:
步骤2
步骤3
步骤4
您可以在控制台中查看错误:
我已经尝试了在这个平台上找到的实现
以及这个stackoverflow的实现
RouteReuseStrategy
是否为子路径做好准备?或者有没有另一种方法来获取路由使用策略
使用包含子路径的路径
角度路由器不必要地复杂,自定义策略继续了这一趋势
您的自定义策略使用route.routerConfig.path
作为存储路由的密钥
它为同一路径存储(覆盖)两条不同的路由person/:id
:
/person/%23123456789%23/编辑
/person/%23123456789%23/查看
第一次存储视图管线,第二次编辑,当您再次打开视图时,最后一次存储的管线是“编辑”,但需要查看
根据路由器的意见,此路由不兼容,它递归检查节点,发现ViewPersonComponent
的routerConfig
与EditPersonComponent
的routerConfig
不同,轰
因此,routerConfig.path
不能用作密钥,或者这是路由器设计问题/限制。我添加了一个解决方法,通过修改自定义RouterUseStrategy中的检索函数,在带有loadChildren的路由上,永远不会检索分离的路由
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!route.routeConfig) return null;
if(route.routeConfig.loadChildren) return null;
return this.handlers[route.routeConfig.path];
}
我不确定它是否是适用于所有场景的完美解决方案,但在我的情况下,它是有效的。以下是一种为strategy类中的routes生成唯一密钥的方法。
我也有类似的问题,但一旦我开始生成唯一密钥,问题就消失了:
private takeFullUrl(route: ActivatedRouteSnapshot) {
let next = route;
// Since navigation is usually relative
// we go down to find out the child to be shown.
while (next.firstChild) {
next = next.firstChild;
}
const segments = [];
// Then build a unique key-path by going to the root.
while (next) {
segments.push(next.url.join('/'));
next = next.parent;
}
return compact(segments.reverse()).join('/');
}
关于这一点,我遇到了一个类似的问题,修改我的唯一键方法解决了这个问题
private routeToUrl(route: ActivatedRouteSnapshot): string {
if (route.url) {
if (route.url.length) {
return route.url.join('/');
} else {
if (typeof route.component === 'function') {
return `[${route.component.name}]`;
} else if (typeof route.component === 'string') {
return `[${route.component}]`;
} else {
return `[null]`;
}
}
} else {
return '(null)';
}
}
private getChildRouteKeys(route:ActivatedRouteSnapshot): string {
let url = this.routeToUrl(route);
return route.children.reduce((fin, cr) => fin += this.getChildRouteKeys(cr), url);
}
private getRouteKey(route: ActivatedRouteSnapshot) {
let url = route.pathFromRoot.map(it => this.routeToUrl(it)).join('/') + '*';
url += route.children.map(cr => this.getChildRouteKeys(cr));
return url;
}
以前我只建立了第一个孩子,现在我只是递归地建立我的钥匙了所有的孩子。我没有编写routeToUrl函数,我是从不久前阅读的一篇关于自定义重用策略的文章中获得的,它没有修改。在我的情况下,我需要检查route.routeConfig.children也在检索方法中:
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!route.routeConfig) return null;
if (route.routeConfig.loadChildren || route.routeConfig.children ) return null;
return this.handlers[route.routeConfig.path];
}
我刚刚在我的应用程序中解决了这个问题,其中包括延迟加载的模块。最后,我不得不解决一个路由重用策略,该策略将路由组件保留在模块内,而不是模块之间
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
export class CustomReuseStrategy implements RouteReuseStrategy {
handlers: { [key: string]: DetachedRouteHandle } = {};
calcKey(route: ActivatedRouteSnapshot) {
return route.pathFromRoot
.map(v => v.url.map(segment => segment.toString()).join('/'))
.filter(url => !!url)
.join('/');
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
this.handlers[this.calcKey(route)] = handle;
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return !!route.routeConfig && !!this.handlers[this.calcKey(route)];
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!route.routeConfig) { return null as any; }
if (route.routeConfig.loadChildren) {
Object.keys(this.handlers).forEach(key => delete this.handlers[key]);
return null as any;
}
return this.handlers[this.calcKey(route)];
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return this.calcKey(curr) === this.calcKey(future);
}
}
如果同一个问题尝试了不同的解决方案,这对我来说是有效的:
import { RouteReuseStrategy} from "@angular/router/";
import { ActivatedRouteSnapshot, DetachedRouteHandle } from "@angular/router";
interface RouteStorageObject {
snapshot: ActivatedRouteSnapshot;
handle: DetachedRouteHandle;
}
export class CacheRouteReuseStrategy implements RouteReuseStrategy {
storedRouteHandles = new Map<string, DetachedRouteHandle>();
allowRetriveCache = {};
storedRoutes: { [key: string]: RouteStorageObject } = {};
shouldReuseRoute( before: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot):boolean {
return before.routeConfig === curr.routeConfig;
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
if (!route.routeConfig || !this.storedRoutes[this.getPath(route)] ) return null as any;
if (route.routeConfig.loadChildren) {
Object.keys(this.storedRoutes).forEach(key => delete this.storedRoutes[key]);
return null as any;
}
return this.storedRoutes[this.getPath(route)].handle;
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
let canAttach: boolean = !!route.routeConfig && !!this.storedRoutes[this.getPath(route)];
if (canAttach) {
let paramsMatch: boolean = this.compareObjects(route.params, this.storedRoutes[this.getPath(route)].snapshot.params);
let queryParamsMatch: boolean = this.compareObjects(route.queryParams, this.storedRoutes[this.getPath(route)].snapshot.queryParams);
return paramsMatch && queryParamsMatch;
} else {
return false;
}
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true
}
store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void {
let storedRoute: RouteStorageObject = {
snapshot: route,
handle: detachedTree
};
if ( detachedTree != null ){
this.storedRoutes[this.getPath(route)] = storedRoute;
}
}
private getPath(route: ActivatedRouteSnapshot): string {
return route.pathFromRoot
.map(v => v.url.map(segment => segment.toString()).join('/'))
.filter(url => !!url)
.join('/');
}
private compareObjects(base: any, compare: any): boolean {
// loop through all properties in base object
for (let baseProperty in base) {
// determine if comparrison object has that property, if not: return false
if (compare.hasOwnProperty(baseProperty)) {
switch(typeof base[baseProperty]) {
// if one is object and other is not: return false
// if they are both objects, recursively call this comparison function
case 'object':
if ( typeof compare[baseProperty] !== 'object' || !this.compareObjects(base[baseProperty], compare[baseProperty]) ) { return false; } break;
// if one is function and other is not: return false
// if both are functions, compare function.toString() results
case 'function':
if ( typeof compare[baseProperty] !== 'function' || base[baseProperty].toString() !== compare[baseProperty].toString() ) { return false; } break;
// otherwise, see if they are equal using coercive comparison
default:
if ( base[baseProperty] != compare[baseProperty] ) { return false; }
}
} else {
return false;
}
}
// returns true only after false HAS NOT BEEN returned through all loops
return true;
}
}
从“@angular/router/”导入{RouteReuseStrategy};
从“@angular/router”导入{ActivatedRouteSnapshot,DetachedRouteHandle};
接口路由器对象{
快照:ActivatedRouteSnapshot;
手柄:可拆卸的手柄;
}
导出类CacheRouterUseStrategy实现RouterUseStrategy{
StoredLoteHandles=新映射();
AllowRetrieveCache={};
StoreDrootes:{[key:string]:RouteStorageObject}={};
shouldReuseRoute(在:ActivatedRouteSnapshot之前,当前:ActivatedRouteSnapshot):布尔值{
在.routeConfig==curr.routeConfig之前返回;
}
检索(路由:ActivatedRouteSnapshot):DetachedRouteHandle | null{
如果(!route.routeConfig | | |!this.storedrootes[this.getPath(route)])返回null作为任意值;
if(route.routeConfig.loadChildren){
Object.keys(this.storedrootes).forEach(key=>delete this.storedrootes[key]);
返回空值,如有;
}
返回此.storedrotes[this.getPath(route)].handle;
}
shouldAttach(路由:ActivatedRouteSnapshot):布尔值{
让canAttach:boolean=!!route.routeConfig&&!!this.storedrootes[this.getPath(route)];
如果(鸭翼){
让paramsMatch:boolean=this.compareObject(route.params,this.storedrootes[this.getPath(route)].snapshot.params);
让queryParamsMatch:boolean=this.compareObject(route.queryParams,this.storedrootes[this.getPath(route)].snapshot.queryParams);
返回paramsMatch&&queryParamsMatch;
}否则{
返回false;
}
}
shouldDetach(路由:ActivatedRouteSnapshot):布尔值{
返回真值
}
存储(路由:ActivatedRouteSnapshot,detachedTree:DetachedRouteHandle):无效{
让StoreDroote:RouteStorageObject={
快照:路线,
把手:分离树
};
if(detachedTree!=null){
this.storedrootes[this.getPath(route)]=storedroote;
}
}
私有getPath(路由:ActivatedRouteSnapshot):字符串{
return route.pathFromRoot
.map(v=>v.url.map(segment=>segment.toString()).join('/'))
.filter(url=>!!url)
。加入(“/”);
}
私有比较对象(基:任意,比较:任意):布尔{
//循环浏览基础对象中的所有属性
for(让baseProperty位于base中){
//确定Comparison对象是否具有该属性,如果没有:返回false
if(比较.hasOwnProperty(baseProperty)){
开关(基本类型[baseProperty]){
//如果一个是对象,另一个不是:返回false
//如果它们都是对象,则递归调用此比较函数
案例“对象”:
if(typeof compare[baseProperty]!='object'| |!this.compareObject(base[baseProperty],compare[baseProperty]){return false;}break;
//如果一个是功能,另一个不是:
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
export class CustomReuseStrategy implements RouteReuseStrategy {
handlers: { [key: string]: DetachedRouteHandle } = {};
calcKey(route: ActivatedRouteSnapshot) {
return route.pathFromRoot
.map(v => v.url.map(segment => segment.toString()).join('/'))
.filter(url => !!url)
.join('/');
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
this.handlers[this.calcKey(route)] = handle;
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return !!route.routeConfig && !!this.handlers[this.calcKey(route)];
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!route.routeConfig) { return null as any; }
if (route.routeConfig.loadChildren) {
Object.keys(this.handlers).forEach(key => delete this.handlers[key]);
return null as any;
}
return this.handlers[this.calcKey(route)];
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return this.calcKey(curr) === this.calcKey(future);
}
}
import { RouteReuseStrategy} from "@angular/router/";
import { ActivatedRouteSnapshot, DetachedRouteHandle } from "@angular/router";
interface RouteStorageObject {
snapshot: ActivatedRouteSnapshot;
handle: DetachedRouteHandle;
}
export class CacheRouteReuseStrategy implements RouteReuseStrategy {
storedRouteHandles = new Map<string, DetachedRouteHandle>();
allowRetriveCache = {};
storedRoutes: { [key: string]: RouteStorageObject } = {};
shouldReuseRoute( before: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot):boolean {
return before.routeConfig === curr.routeConfig;
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
if (!route.routeConfig || !this.storedRoutes[this.getPath(route)] ) return null as any;
if (route.routeConfig.loadChildren) {
Object.keys(this.storedRoutes).forEach(key => delete this.storedRoutes[key]);
return null as any;
}
return this.storedRoutes[this.getPath(route)].handle;
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
let canAttach: boolean = !!route.routeConfig && !!this.storedRoutes[this.getPath(route)];
if (canAttach) {
let paramsMatch: boolean = this.compareObjects(route.params, this.storedRoutes[this.getPath(route)].snapshot.params);
let queryParamsMatch: boolean = this.compareObjects(route.queryParams, this.storedRoutes[this.getPath(route)].snapshot.queryParams);
return paramsMatch && queryParamsMatch;
} else {
return false;
}
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true
}
store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void {
let storedRoute: RouteStorageObject = {
snapshot: route,
handle: detachedTree
};
if ( detachedTree != null ){
this.storedRoutes[this.getPath(route)] = storedRoute;
}
}
private getPath(route: ActivatedRouteSnapshot): string {
return route.pathFromRoot
.map(v => v.url.map(segment => segment.toString()).join('/'))
.filter(url => !!url)
.join('/');
}
private compareObjects(base: any, compare: any): boolean {
// loop through all properties in base object
for (let baseProperty in base) {
// determine if comparrison object has that property, if not: return false
if (compare.hasOwnProperty(baseProperty)) {
switch(typeof base[baseProperty]) {
// if one is object and other is not: return false
// if they are both objects, recursively call this comparison function
case 'object':
if ( typeof compare[baseProperty] !== 'object' || !this.compareObjects(base[baseProperty], compare[baseProperty]) ) { return false; } break;
// if one is function and other is not: return false
// if both are functions, compare function.toString() results
case 'function':
if ( typeof compare[baseProperty] !== 'function' || base[baseProperty].toString() !== compare[baseProperty].toString() ) { return false; } break;
// otherwise, see if they are equal using coercive comparison
default:
if ( base[baseProperty] != compare[baseProperty] ) { return false; }
}
} else {
return false;
}
}
// returns true only after false HAS NOT BEEN returned through all loops
return true;
}
}