我有一个自定义router-outlet
来缓存组件。
代码是:https://gist.github.com/waeljammal/467286d64f59f8340a93#file-persistentrouteroutlet-ts-L75
代码示例使用组件类型作为缓存键 - 但是,我需要通过URL缓存,以便我可以缓存同一组件的两个不同实例。
有没有办法根据ComponentInstruction
获取网址?
答案 0 :(得分:3)
所以我现在就开始工作了。错误在于我将PersistenRouterOutlet
应用于顶级路由器。
考虑urlPath: /space/settings/general
。
我的App组件中有一个router-outlet
,然后我有WorkspaceComponent
绑定到space
urlPath
。
WorkspaceComponent
然后托管自己的router-outlet
并定义一组子路由 - 从URL中,它会侦听settings/general
部分。
要解决此问题,我从我的应用组件中删除了persistent-router-outlet
(替换为默认的router-outlet
。
然后在WorkspaceComponent
的模板中,我将默认router-outlet
替换为persistent-router-outlet
。
最后但并非最不重要的 - 子路由器(在WorkspaceComponent
内)实例化的组件类型需要实现CanReuse
接口,并实现routerCanReuse
方法:
routerCanReuse(next: ComponentInstruction, prev: ComponentInstruction) {
return next.urlPath === prev.urlPath;
}
PersistentRouterOutlet
的代码在这里:
"use strict";
import * as hookMod from 'angular2/src/router/lifecycle/lifecycle_annotations';
import * as routerMod from 'angular2/src/router/router';
import {isBlank, isPresent} from 'angular2/src/facade/lang';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {BaseException} from 'angular2/src/facade/exceptions';
import {
ElementRef, DynamicComponentLoader, Directive, Injector, provide, ComponentRef, Attribute
} from 'angular2/core';
import {
ComponentInstruction, CanReuse, OnReuse, CanDeactivate,
RouterOutlet, OnActivate, Router, RouteData, RouteParams, OnDeactivate
} from 'angular2/router';
import {hasLifecycleHook} from 'angular2/src/router/lifecycle/route_lifecycle_reflector';
/**
* Reference Cache Entry
*/
class RefCacheItem {
constructor(public componentRef: ComponentRef) {
}
}
/**
* Reference Cache
*/
class RefCache {
private cache: any = {};
public getRef(type: any) {
return this.cache[type];
}
public addRef(type: any, ref: RefCacheItem) {
this.cache[type] = ref;
}
public hasRef(type: any): boolean {
return !isBlank(this.cache[type]);
}
}
/**
* An outlet that persists the child views and re-uses their components.
*
* @author Wael Jammal
*/
@Directive({ selector: 'persistent-router-outlet' })
export class PersistentRouterOutlet extends RouterOutlet {
private currentInstruction: ComponentInstruction;
private currentElementRef;
private refCache: RefCache = new RefCache();
private resolveToTrue = PromiseWrapper.resolve(true);
private currentComponentRef: ComponentRef;
constructor(elementRef: ElementRef,
private loader: DynamicComponentLoader,
private parentRouter: Router,
@Attribute('name') nameAttr: string) {
super(elementRef, loader, parentRouter, nameAttr);
this.currentElementRef = elementRef;
}
/**
* Called by the Router to instantiate a new component during the commit phase of a navigation.
* This method in turn is responsible for calling the `routerOnActivate` hook of its child.
*/
public activate(nextInstruction: ComponentInstruction): Promise<any> {
let previousInstruction = this.currentInstruction;
this.currentInstruction = nextInstruction;
if (!this.refCache.hasRef(nextInstruction.urlPath)) {
let componentType = nextInstruction.componentType;
let childRouter = this.parentRouter.childRouter(componentType);
let providers = Injector.resolve([
provide(RouteData, { useValue: nextInstruction.routeData }),
provide(RouteParams, { useValue: new RouteParams(nextInstruction.params) }),
provide(routerMod.Router, { useValue: childRouter })
]);
return this.loader.loadNextToLocation(componentType, this.currentElementRef, providers)
.then((componentRef) => {
this.refCache.addRef(nextInstruction.urlPath, new RefCacheItem(componentRef));
this.currentComponentRef = componentRef;
if (hasLifecycleHook(hookMod.routerOnActivate, componentType)) {
return (<OnActivate>componentRef.instance)
.routerOnActivate(nextInstruction, previousInstruction);
}
});
}
else {
let ref = this.refCache.getRef(nextInstruction.urlPath);
ref.componentRef.location.nativeElement.hidden = false;
this.currentComponentRef = ref.componentRef;
return PromiseWrapper.resolve(
hasLifecycleHook(hookMod.routerOnReuse, this.currentInstruction.componentType) ?
(<OnReuse>ref.componentRef.instance).routerOnReuse(nextInstruction, previousInstruction) : true
);
}
}
/**
* Called by the Router during the commit phase of a navigation when an outlet
* reuses a component between different routes.
* This method in turn is responsible for calling the `routerOnReuse` hook of its child.
*/
public reuse(nextInstruction: ComponentInstruction): Promise<any> {
let previousInstruction = this.currentInstruction;
this.currentInstruction = nextInstruction;
if (isBlank(this.currentComponentRef)) {
throw new BaseException(`Cannot reuse an outlet that does not contain a component.`);
}
let ref = this.refCache.getRef(nextInstruction.urlPath);
let currentRef = ref ? ref.componentRef : null;
return PromiseWrapper.resolve(
hasLifecycleHook(hookMod.routerOnReuse, this.currentInstruction.componentType) ?
(<OnReuse>currentRef.instance).routerOnReuse(nextInstruction, previousInstruction) : true
);
}
/**
* Called by the Router when an outlet disposes of a component's contents.
* This method in turn is responsible for calling the `routerOnDeactivate` hook of its child.
*/
public deactivate(nextInstruction: ComponentInstruction): Promise<any> {
let next = this.resolveToTrue;
let ref = this.currentComponentRef;
if (isPresent(ref) && isPresent(this.currentInstruction) &&
hasLifecycleHook(hookMod.routerOnDeactivate, this.currentInstruction.componentType)) {
next = PromiseWrapper.resolve(
(<OnDeactivate>ref.instance)
.routerOnDeactivate(nextInstruction, this.currentInstruction));
}
return next.then(() => {
if (isPresent(ref)) {
ref.location.nativeElement.hidden = true;
}
});
}
/**
* Called by the Router during recognition phase of a navigation.
*
* If this resolves to `false`, the given navigation is cancelled.
*
* This method delegates to the child component's `routerCanDeactivate` hook if it exists,
* and otherwise resolves to true.
*/
public routerCanDeactivate(nextInstruction: ComponentInstruction): Promise<boolean> {
if (isBlank(this.currentInstruction)) {
return this.resolveToTrue;
}
let ref = this.currentComponentRef;
if (!ref) {
let foundRef = this.refCache.getRef(this.currentInstruction.urlPath);
ref = foundRef ? foundRef.componentRef : null;
}
if (hasLifecycleHook(hookMod.routerCanDeactivate, this.currentInstruction.componentType)) {
return PromiseWrapper.resolve(
(<CanDeactivate>ref.instance)
.routerCanDeactivate(nextInstruction, this.currentInstruction));
}
return this.resolveToTrue;
}
/**
* Called by the Router during recognition phase of a navigation.
*
* If the new child component has a different Type than the existing child component,
* this will resolve to `false`. You can't reuse an old component when the new component
* is of a different Type.
*
* Otherwise, this method delegates to the child component's `routerCanReuse` hook if it exists,
* or resolves to true if the hook is not present.
*/
public routerCanReuse(nextInstruction: ComponentInstruction): Promise<boolean> {
let result;
let ref = this.currentComponentRef;
if (!ref) {
let foundRef = this.refCache.getRef(nextInstruction.urlPath);
ref = foundRef ? foundRef.componentRef : null;
}
if (isBlank(this.currentInstruction) || !this.refCache.hasRef(nextInstruction.urlPath) || this.currentInstruction.componentType !== nextInstruction.componentType) {
result = false;
}
else if (hasLifecycleHook(hookMod.routerCanReuse, this.currentInstruction.componentType)) {
result = (<CanReuse>ref.instance)
.routerCanReuse(nextInstruction, this.currentInstruction);
}
else {
result = nextInstruction === this.currentInstruction ||
(isPresent(nextInstruction.params) && isPresent(this.currentInstruction.params) &&
StringMapWrapper.equals(nextInstruction.params, this.currentInstruction.params));
}
return PromiseWrapper.resolve(result);
}
}