-
-
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 ?