2016-03-19 10 views
3

Ich habe eine benutzerdefinierte router-outlet, die Komponenten zwischenspeichert. dieseAngular 2 erhalten URL von ComponentInstruction

Der Code für das heißt: https://gist.github.com/waeljammal/467286d64f59f8340a93#file-persistentrouteroutlet-ts-L75

Das Codebeispiel wird den Komponententyp als Cache-Schlüssel - aber ich brauche Mine von der URL in der Cache, so dass ich zwei verschiedene Instanzen derselben Komponente cachen .

Gibt es eine Möglichkeit, die URL basierend auf der ComponentInstruction zu bekommen?

+0

haben Sie überprüft, mit 'urlPath'? – micronyks

+0

Ja, das gibt mir nur das erste Segment. Wenn die URL also '/ space/settings/general 'lauten soll, bekomme ich nur' space' und die 'routeData',' params' und 'urlParams' Eigenschaften sind alle leer. – MartinHN

Antwort

3

Also habe ich das jetzt funktioniert. Der Fehler war, dass ich die PersistenRouterOutlet an den Top-Level-Router anwendete.

Betrachten Sie die urlPath: /space/settings/general.

Ich habe ein router-outlet in meiner App-Komponente, und dann habe ich ein WorkspaceComponent, die die spaceurlPath gebunden war.

Die WorkspaceComponent hostet dann eine eigene router-outlet und definiert eine Reihe von untergeordneten Routen - von der URL hört dies auf den settings/general Teil.

Um dies zu lösen, ich persistent-router-outlet aus meiner App-Komponente entfernt (mit dem router-outlet Standard ersetzt

dann in der Vorlage für die WorkspaceComponent ich die Standard-router-outlet mit persistent-router-outlet ersetzt

Last but not least.. - Die Komponententypen, die vom untergeordneten Router (innerhalb WorkspaceComponent) instanziiert werden, müssen die Schnittstelle CanReuse implementieren und die routerCanReuse-Methode implementieren:

routerCanReuse(next: ComponentInstruction, prev: ComponentInstruction) { 
    return next.urlPath === prev.urlPath; 
} 

Der Code für die PersistentRouterOutlet ist hier:

"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); 
    } 
}