오류: 다른 경로에서 생성된 ActivatedRouteSnapshot을 다시 연결할 수 없습니다.
나는 그것을 구현하기 위해 노력하고 있습니다.RouteReuseStrategy
클래스입니다. 최상위 경로로 이동할 때 잘 작동합니다.
경로에 자식 경로가 있고 자식 경로로 이동한 후 최상위 경로로 다시 이동하면 다음 오류가 발생합니다.
오류: 발견되지 않음(약속 없음):오류: 다른 경로에서 생성된 ActivatedRouteSnapshot을 다시 연결할 수 없습니다.
오류를 보여주기 위해 플런커를 만들었습니다.IE 11에서는 플런커가 작동하지 않습니다. 최신 버전의 Chrome에서 확인하십시오.
오류를 재현하는 단계:
콘솔에서 오류를 볼 수 있습니다.
이 문서에서 발견된 구현을 시도해 보았습니다.
export class CustomReuseStrategy implements RouteReuseStrategy {
handlers: {[key: string]: DetachedRouteHandle} = {};
shouldDetach(route: ActivatedRouteSnapshot): boolean {
console.debug('CustomReuseStrategy:shouldDetach', route);
return true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
console.debug('CustomReuseStrategy:store', route, handle);
this.handlers[route.routeConfig.path] = handle;
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
console.debug('CustomReuseStrategy:shouldAttach', route);
return !!route.routeConfig && !!this.handlers[route.routeConfig.path];
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
console.debug('CustomReuseStrategy:retrieve', route);
if (!route.routeConfig) return null;
return this.handlers[route.routeConfig.path];
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
console.debug('CustomReuseStrategy:shouldReuseRoute', future, curr);
return future.routeConfig === curr.routeConfig;
}
}
그리고 이 스택 오버플로 답변의 구현은
/**
* reuse-strategy.ts
* by corbfon 1/6/17
*/
import { ActivatedRouteSnapshot, RouteReuseStrategy, DetachedRouteHandle } from '@angular/router';
/** Interface for object which can store both:
* An ActivatedRouteSnapshot, which is useful for determining whether or not you should attach a route (see this.shouldAttach)
* A DetachedRouteHandle, which is offered up by this.retrieve, in the case that you do want to attach the stored route
*/
interface RouteStorageObject {
snapshot: ActivatedRouteSnapshot;
handle: DetachedRouteHandle;
}
export class CustomReuseStrategy implements RouteReuseStrategy {
/**
* Object which will store RouteStorageObjects indexed by keys
* The keys will all be a path (as in route.routeConfig.path)
* This allows us to see if we've got a route stored for the requested path
*/
storedRoutes: { [key: string]: RouteStorageObject } = {};
/**
* Decides when the route should be stored
* If the route should be stored, I believe the boolean is indicating to a controller whether or not to fire this.store
* _When_ it is called though does not particularly matter, just know that this determines whether or not we store the route
* An idea of what to do here: check the route.routeConfig.path to see if it is a path you would like to store
* @param route This is, at least as I understand it, the route that the user is currently on, and we would like to know if we want to store it
* @returns boolean indicating that we want to (true) or do not want to (false) store that route
*/
shouldDetach(route: ActivatedRouteSnapshot): boolean {
let detach: boolean = true;
console.log("detaching", route, "return: ", detach);
return detach;
}
/**
* Constructs object of type `RouteStorageObject` to store, and then stores it for later attachment
* @param route This is stored for later comparison to requested routes, see `this.shouldAttach`
* @param handle Later to be retrieved by this.retrieve, and offered up to whatever controller is using this class
*/
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
let storedRoute: RouteStorageObject = {
snapshot: route,
handle: handle
};
console.log("store:", storedRoute, "into: ", this.storedRoutes);
// routes are stored by path - the key is the path name, and the handle is stored under it so that you can only ever have one object stored for a single path
this.storedRoutes[route.routeConfig.path] = storedRoute;
}
/**
* Determines whether or not there is a stored route and, if there is, whether or not it should be rendered in place of requested route
* @param route The route the user requested
* @returns boolean indicating whether or not to render the stored route
*/
shouldAttach(route: ActivatedRouteSnapshot): boolean {
// this will be true if the route has been stored before
let canAttach: boolean = !!route.routeConfig && !!this.storedRoutes[route.routeConfig.path];
// this decides whether the route already stored should be rendered in place of the requested route, and is the return value
// at this point we already know that the paths match because the storedResults key is the route.routeConfig.path
// so, if the route.params and route.queryParams also match, then we should reuse the component
if (canAttach) {
let willAttach: boolean = true;
console.log("param comparison:");
console.log(this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params));
console.log("query param comparison");
console.log(this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams));
let paramsMatch: boolean = this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params);
let queryParamsMatch: boolean = this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams);
console.log("deciding to attach...", route, "does it match?", this.storedRoutes[route.routeConfig.path].snapshot, "return: ", paramsMatch && queryParamsMatch);
return paramsMatch && queryParamsMatch;
} else {
return false;
}
}
/**
* Finds the locally stored instance of the requested route, if it exists, and returns it
* @param route New route the user has requested
* @returns DetachedRouteHandle object which can be used to render the component
*/
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
// return null if the path does not have a routerConfig OR if there is no stored route for that routerConfig
if (!route.routeConfig || !this.storedRoutes[route.routeConfig.path]) return null;
console.log("retrieving", "return: ", this.storedRoutes[route.routeConfig.path]);
/** returns handle when the route.routeConfig.path is already stored */
return this.storedRoutes[route.routeConfig.path].handle;
}
/**
* Determines whether or not the current route should be reused
* @param future The route the user is going to, as triggered by the router
* @param curr The route the user is currently on
* @returns boolean basically indicating true if the user intends to leave the current route
*/
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
console.log("deciding to reuse", "future", future.routeConfig, "current", curr.routeConfig, "return: ", future.routeConfig === curr.routeConfig);
return future.routeConfig === curr.routeConfig;
}
/**
* This nasty bugger finds out whether the objects are _traditionally_ equal to each other, like you might assume someone else would have put this function in vanilla JS already
* One thing to note is that it uses coercive comparison (==) on properties which both objects have, not strict comparison (===)
* @param base The base object which you would like to compare another object to
* @param compare The object to compare to base
* @returns boolean indicating whether or not the objects have all the same properties and those properties are ==
*/
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;
}
}
아이즈RouteReuseStrategy
출산 준비가 된paths
아니면 다른 방법이 있을까요?RouteReuseStrategy
하위 항목이 포함된 경로로 작업paths
사용자 지정 RouteRuseStrategy에서 검색 기능을 수정하여 loadChildren이 있는 경로에서 분리된 경로를 검색하지 않는 해결 방법을 추가했습니다.
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!route.routeConfig) return null;
if(route.routeConfig.loadChildren) return null;
return this.handlers[route.routeConfig.path];
}
모든 시나리오에 완벽한 솔루션인지는 모르겠지만 저의 경우에는 효과가 있습니다.
각도 라우터는 불필요하게 복잡하며 사용자 지정 전략은 이러한 추세를 계속합니다.
사용자 정의 전략은 다음과 같습니다.route.routerConfig.path
저장된 경로의 키로 사용됩니다.
동일한 경로에 대해 서로 다른 두 경로를 저장(덮어쓰기)합니다.person/:id
:
/person/%23123456789%23/edit
/person/%23123456789%23/view
뷰 경로가 처음 저장된 경우, 두 번째 편집된 경우, 뷰를 다시 열면 마지막으로 저장된 경로가 편집되지만 뷰가 필요합니다.
이 경로는 라우터 의견에 따라 호환되지 않습니다. 노드를 재귀적으로 확인하고 다음을 확인합니다.routerConfig
위해서ViewPersonComponent
와 동일하지 않습니다.routerConfig
위해서EditPersonComponent
쾅!
그래서 어느쪽이든routerConfig.path
키로 사용해서는 안 됩니다. 그렇지 않으면 라우터 설계 문제/제한입니다.
전략 클래스에서 경로에 대한 고유 키를 생성하는 방법은 다음과 같습니다.저도 비슷한 문제가 있었지만 고유한 키를 생성하기 시작하자 문제가 사라졌습니다.
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('/');
}
자세한 내용은 https://github.com/angular/angular/issues/13869#issuecomment-344403045 을 참조하십시오.
저도 비슷한 문제에 부딪혔고, 저만의 키 방식을 수정하여 해결했습니다.
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 함수를 작성하지 않았습니다. 얼마 전에 커스텀 재사용 전략에 대해 읽은 기사에서 얻었으며 수정되지 않았습니다.
저는 이것을 게으르게 로드된 모듈이 포함된 앱 내에서 방금 해결했습니다.결국 저는 모듈 내에서 라우팅된 구성 요소를 보존하지만 모듈 간에는 보존하지 않는 루트 재사용 전략으로 해결해야 했습니다.
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);
}
}
나의 경우 route.routeConfig.children도 retrie 메서드에서 확인해야 합니다.
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 { 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;
}
}
언급URL : https://stackoverflow.com/questions/41584664/error-cannot-reattach-activatedroutesnapshot-created-from-a-different-route
'programing' 카테고리의 다른 글
MongoDB로 보고하는 방법은 무엇입니까? (0) | 2023.07.08 |
---|---|
numpy 배열을 mongodb에 저장하는 중 (0) | 2023.07.08 |
CouchDB, MongoDB 및 Redis 중 Node.js로 시작하기에 좋은 데이터베이스는 무엇입니까? (0) | 2023.07.08 |
SQL - 열의 고유한 조합을 카운트하는 방법 (0) | 2023.07.08 |
Oracle Client란 무엇입니까? (0) | 2023.07.03 |