From 52f13088e1607bcb2be3011db79793a50e7fd3d6 Mon Sep 17 00:00:00 2001 From: Chris Thielen Date: Mon, 27 Jun 2016 09:54:03 -0500 Subject: [PATCH] feat(Transition): expose the current `UiRouter` object as a public property feat(UiInjector): add `getAsync` method refactor(UiInjector): rename UIRInjector to UiInjector --- src/common/interface.ts | 21 ++++++++++++++++++++- src/hooks/onEnterExitRetain.ts | 4 ++-- src/hooks/redirectTo.ts | 4 ++-- src/hooks/url.ts | 4 ++-- src/hooks/views.ts | 4 ++-- src/resolve/resolveContext.ts | 32 +++++++++++++++++++++++--------- src/state/interface.ts | 4 ++-- src/transition/interface.ts | 6 +++--- src/transition/transition.ts | 32 +++++++++++++++++++++++++++++++- 9 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/common/interface.ts b/src/common/interface.ts index adc8ecb3f..861b2855a 100644 --- a/src/common/interface.ts +++ b/src/common/interface.ts @@ -1,7 +1,7 @@ /** * An interface for getting values from dependency injection. */ -export interface UIRInjector { +export interface UiInjector { /** * Gets a value from the injector * @@ -28,4 +28,23 @@ export interface UIRInjector { * @return the Dependency Injection value that matches the key */ get(key: any): any; + + /** + * Asynchronously gets a value from the injector + * + * Returns a promise for a value from the injector. + * Returns resolve values and/or values from the native injector (ng1/ng2). + * + * @example + * ```js + * + * return injector.getAsync('myResolve').then(value => { + * if (value === 'declined') return false; + * }); + * ``` + * + * @param key the key for the value to get. May be a string or arbitrary object. + * @return a Promise for the Dependency Injection value that matches the key + */ + getAsync(key: any): any; } \ No newline at end of file diff --git a/src/hooks/onEnterExitRetain.ts b/src/hooks/onEnterExitRetain.ts index 5514838d2..89ca87338 100644 --- a/src/hooks/onEnterExitRetain.ts +++ b/src/hooks/onEnterExitRetain.ts @@ -1,10 +1,10 @@ /** @module state */ /** for typedoc */ import {TransitionStateHookFn} from "../transition/interface"; -import {UIRInjector} from "../common/interface"; +import {UiInjector} from "../common/interface"; import {State} from "../state/stateObject"; import {Transition} from "../transition/transition"; export function makeEnterExitRetainHook(hookName: string): TransitionStateHookFn { - return (transition: Transition, injector: UIRInjector, state: State) => + return (transition: Transition, injector: UiInjector, state: State) => state[hookName](transition, injector, state); } diff --git a/src/hooks/redirectTo.ts b/src/hooks/redirectTo.ts index 4c14c77e7..e9ca6e05d 100644 --- a/src/hooks/redirectTo.ts +++ b/src/hooks/redirectTo.ts @@ -1,5 +1,5 @@ import {isString, isFunction} from "../common/predicates" -import {UIRInjector} from "../common/interface"; +import {UiInjector} from "../common/interface"; import {Transition} from "../transition/transition"; import {UiRouter} from "../router"; import {services} from "../common/coreservices"; @@ -10,7 +10,7 @@ import {TargetState} from "../state/targetState"; * * See [[StateDeclaration.redirectTo]] */ -export const redirectToHook = (transition: Transition, $injector: UIRInjector) => { +export const redirectToHook = (transition: Transition, $injector: UiInjector) => { let redirect = transition.to().redirectTo; if (!redirect) return; diff --git a/src/hooks/url.ts b/src/hooks/url.ts index a7366ce06..1018b3aee 100644 --- a/src/hooks/url.ts +++ b/src/hooks/url.ts @@ -1,10 +1,10 @@ import {UrlRouter} from "../url/urlRouter"; import {StateService} from "../state/stateService"; import {Transition} from "../transition/transition"; -import {UIRInjector} from "../common/interface"; +import {UiInjector} from "../common/interface"; import {UiRouter} from "../router"; -export function updateUrl(transition: Transition, injector: UIRInjector) { +export function updateUrl(transition: Transition, injector: UiInjector) { let options = transition.options(); var router: UiRouter = injector.get(UiRouter); let $state: StateService = router.stateService; diff --git a/src/hooks/views.ts b/src/hooks/views.ts index 59e2b61ec..8bc1fd290 100644 --- a/src/hooks/views.ts +++ b/src/hooks/views.ts @@ -5,7 +5,7 @@ import {Transition} from "../transition/transition"; import {ViewService} from "../view/view"; import {ViewConfig} from "../view/interface"; import {TransitionService} from "../transition/transitionService"; -import {UIRInjector} from "../common/interface"; +import {UiInjector} from "../common/interface"; import {UiRouter} from "../router"; @@ -16,7 +16,7 @@ export function loadEnteringViews(transition) { return services.$q.all(enteringViews.map(view => view.load())).then(noop); } -export function activateViews(transition: Transition, injector: UIRInjector) { +export function activateViews(transition: Transition, injector: UiInjector) { let enteringViews = transition.views("entering"); let exitingViews = transition.views("exiting"); if (!enteringViews.length && !exitingViews.length) return; diff --git a/src/resolve/resolveContext.ts b/src/resolve/resolveContext.ts index 82b7b0907..00bf350cc 100644 --- a/src/resolve/resolveContext.ts +++ b/src/resolve/resolveContext.ts @@ -11,6 +11,7 @@ import {State} from "../state/stateObject"; import {PathFactory} from "../path/pathFactory"; import {stringify} from "../common/strings"; import {Transition} from "../transition/transition"; +import {UiInjector} from "../common/interface"; var when = resolvePolicies.when; const ALL_WHENS = [when.EAGER, when.LAZY]; @@ -129,15 +130,8 @@ export class ResolveContext { return services.$q.all(promises); } - injector(): { get(any): any } { - - let get = (token: any) => { - var resolvable = this.getResolvable(token); - if (resolvable) return resolvable.data; - return services.$injector.get(token); - }; - - return { get }; + injector(): UiInjector { + return new UiInjectorImpl(this); } findNode(resolvable: Resolvable): PathNode { @@ -173,3 +167,23 @@ export class ResolveContext { return resolvable.deps.map(getDependency); } } + +class UiInjectorImpl implements UiInjector { + constructor(public context: ResolveContext) { } + get(token: any) { + var resolvable = this.context.getResolvable(token); + if (resolvable) { + if (!resolvable.resolved) { + throw new Error("Resolvable async .get() not complete:" + stringify(resolvable.token)) + } + return resolvable.data; + } + return services.$injector.get(token); + } + + getAsync(token: any) { + var resolvable = this.context.getResolvable(token); + if (resolvable) return resolvable.get(this.context); + return services.$q.when(services.$injector.get(token)); + } +} \ No newline at end of file diff --git a/src/state/interface.ts b/src/state/interface.ts index 7212c0211..eef0ee325 100644 --- a/src/state/interface.ts +++ b/src/state/interface.ts @@ -8,7 +8,7 @@ import {Transition} from "../transition/transition"; import {TransitionStateHookFn} from "../transition/interface"; import {ResolvePolicy, ResolvableLiteral} from "../resolve/interface"; import {Resolvable} from "../resolve/resolvable"; -import {UIRInjector} from "../common/interface"; +import {UiInjector} from "../common/interface"; import {TargetState} from "./targetState"; export type StateOrName = (string|StateDeclaration|State); @@ -452,7 +452,7 @@ export interface StateDeclaration { * }) */ redirectTo?: ( - ($transition$: Transition, $injector: UIRInjector) => TargetState | + ($transition$: Transition, $injector: UiInjector) => TargetState | { state: (string|StateDeclaration), params: { [key: string]: any }} | string ) diff --git a/src/transition/interface.ts b/src/transition/interface.ts index d5bda8782..cd78bec0e 100644 --- a/src/transition/interface.ts +++ b/src/transition/interface.ts @@ -6,7 +6,7 @@ import {Transition} from "./transition"; import {State} from "../state/stateObject"; import {PathNode} from "../path/node"; import {TargetState} from "../state/targetState"; -import {UIRInjector} from "../common/interface"; +import {UiInjector} from "../common/interface"; /** * The TransitionOptions object can be used to change the behavior of a transition. @@ -183,7 +183,7 @@ export type IHookRegistration = (matchCriteria: HookMatchCriteria, callback: Hoo * - [[IHookRegistry.onError]] */ export interface TransitionHookFn { - (transition: Transition, injector: UIRInjector) : HookResult + (transition: Transition, injector: UiInjector) : HookResult } /** @@ -208,7 +208,7 @@ export interface TransitionHookFn { * - [[IHookRegistry.onExit]] */ export interface TransitionStateHookFn { - (transition: Transition, injector: UIRInjector, state: State) : HookResult + (transition: Transition, injector: UiInjector, state: State) : HookResult } export type HookFn = (TransitionHookFn|TransitionStateHookFn); diff --git a/src/transition/transition.ts b/src/transition/transition.ts index 6b6a714d3..0b1f36d2c 100644 --- a/src/transition/transition.ts +++ b/src/transition/transition.ts @@ -25,6 +25,7 @@ import {Rejection} from "./rejectFactory"; import {ResolveContext} from "../resolve/resolveContext"; import {UiRouter} from "../router"; import {Globals} from "../globals"; +import {UiInjector} from "../common/interface"; let transitionCount = 0; @@ -44,6 +45,14 @@ export class Transition implements IHookRegistry { $id: number; success: boolean; + /** + * A reference to the [[UiRouter]] instance + * + * This reference can be used to access the router services, such as the [[StateService]] + */ + router: UiRouter; + + /** @hidden */ private _deferred = services.$q.defer(); /** * This promise is resolved or rejected based on the outcome of the Transition. @@ -140,7 +149,8 @@ export class Transition implements IHookRegistry { * @param targetState The target state and parameters being transitioned to (also, the transition options) * @param router The [[UiRouter]] instance */ - constructor(fromPath: PathNode[], targetState: TargetState, private router: UiRouter) { + constructor(fromPath: PathNode[], targetState: TargetState, router: UiRouter) { + this.router = router; if (!targetState.valid()) { throw new Error(targetState.error()); } @@ -219,6 +229,26 @@ export class Transition implements IHookRegistry { return this._treeChanges[pathname].map(prop("paramValues")).reduce(mergeR, {}); } + + /** + * Creates a [[UiInjector]] Dependency Injector + * + * Returns a Dependency Injector for the Transition's target state (to state). + * The injector provides resolve values which the target state has access to. + * + * The `UiInjector` can also provide values from the native root/global injector (ng1/ng2). + * + * If a `state` is provided, the injector that is returned will be limited to resolve values that the provided state has access to. + * + * @param state Limits the resolves provided to only the resolves the provided state has access to. + * @returns a [[UiInjector]] + */ + injector(state?: StateOrName): UiInjector { + let path: PathNode[] = this.treeChanges().to; + if (state) path = PathFactory.subPath(path, node => node.state === state || node.state.name === state); + return new ResolveContext(path).injector(); + } + /** * Gets all available resolve tokens (keys) *