-
-
Save waeljammal/467286d64f59f8340a93 to your computer and use it in GitHub Desktop.
import * as hookMod from 'angular2/src/router/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/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.componentType)) { | |
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.componentType, 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.componentType); | |
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.componentType); | |
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.componentType); | |
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.componentType); | |
ref = foundRef ? foundRef.componentRef : null; | |
} | |
if (isBlank(this.currentInstruction) || | |
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); | |
} | |
} |
Hi Wael,
It appears that the RouterAPI has changed significantly.
The RouterOutlet methods look very different:
constructor(parentOutletMap: RouterOutletMap, location: ViewContainerRef, resolver: ComponentFactoryResolver, name: string)
outletMap : RouterOutletMap
activateEvents : EventEmitter
deactivateEvents : EventEmitter
ngOnDestroy() : void
locationInjector : Injector
locationFactoryResolver : ComponentFactoryResolver
isActivated : boolean
component : Object
activatedRoute : ActivatedRoute
detach() : ComponentRef
attach(ref: ComponentRef, activatedRoute: ActivatedRoute)
deactivate() : void
activate(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver, injector: Injector, providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap) : void
activateWith(activatedRoute: ActivatedRoute, resolver?: ComponentFactoryResolver|, outletMap: RouterOutletMap)
Can you please post an updated version of your solution. This would be immensely helpful to everyone using Angular2.
Thanks,
Vinay
hi, any progress for RC 1 ?