From d3bd3328024b0369f44103eae30eb1be9fddecb5 Mon Sep 17 00:00:00 2001 From: Chris Thielen Date: Wed, 21 Sep 2016 10:41:47 -0500 Subject: [PATCH] fix(ng2.NgModule): Allow apps with no forChild modules fix(ResolveContext): Scope native injector in resolve tree. - Build injector() using scoped native injector refactor(ng2.NgModule): Switch from NG2_INJECTOR_TOKEN to NATIVE_INJECTOR_TOKEN - Move token to core Closes #3009 --- src/ng2/directives/uiView.ts | 5 ++--- src/ng2/lazyLoadNgModule.ts | 12 ++++++------ src/ng2/providers.ts | 11 ++++++----- src/ng2/uiRouterConfig.ts | 6 ++---- src/ng2/uiRouterNgModule.ts | 23 +++++++++++------------ src/resolve/resolveContext.ts | 29 +++++++++++++++++------------ 6 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/ng2/directives/uiView.ts b/src/ng2/directives/uiView.ts index 0fc55998f..8f8b2d8c1 100755 --- a/src/ng2/directives/uiView.ts +++ b/src/ng2/directives/uiView.ts @@ -7,9 +7,8 @@ import { import {UIRouter} from "../../router"; import {trace} from "../../common/trace"; import {ViewContext, ViewConfig, ActiveUIView} from "../../view/interface"; -import {NG2_INJECTOR_TOKEN} from "../interface"; import {Ng2ViewConfig} from "../statebuilders/views"; -import {ResolveContext} from "../../resolve/resolveContext"; +import {ResolveContext, NATIVE_INJECTOR_TOKEN} from "../../resolve/resolveContext"; import {flattenR} from "../../common/common"; import {MergeInjector} from "../mergeInjector"; @@ -227,7 +226,7 @@ export class UIView { newProviders.push({ provide: UIView.PARENT_INJECT, useValue: parentInject }); let parentComponentInjector = this.viewContainerRef.injector; - let moduleInjector = context.getResolvable(NG2_INJECTOR_TOKEN).data; + let moduleInjector = context.getResolvable(NATIVE_INJECTOR_TOKEN).data; let mergedParentInjector = new MergeInjector(moduleInjector, parentComponentInjector); return ReflectiveInjector.resolveAndCreate(newProviders, mergedParentInjector); diff --git a/src/ng2/lazyLoadNgModule.ts b/src/ng2/lazyLoadNgModule.ts index c6adc7ee6..d9edd1126 100644 --- a/src/ng2/lazyLoadNgModule.ts +++ b/src/ng2/lazyLoadNgModule.ts @@ -1,14 +1,14 @@ /** @module ng2 */ /** */ import {NgModuleFactoryLoader, NgModuleRef, Injector, NgModuleFactory} from "@angular/core"; -import {NG2_INJECTOR_TOKEN} from "./interface"; import {LazyLoadResult} from "../state/interface"; import {Transition} from "../transition/transition"; -import {RootModule, ChildModule, UIROUTER_ROOT_MODULE, UIROUTER_CHILD_MODULE} from "./uiRouterNgModule"; +import {RootModule, StatesModule, UIROUTER_ROOT_MODULE, UIROUTER_MODULE_TOKEN} from "./uiRouterNgModule"; import {applyModuleConfig} from "./uiRouterConfig"; import {UIRouter} from "../router"; import {Resolvable} from "../resolve/resolvable"; +import {NATIVE_INJECTOR_TOKEN} from "../resolve/resolveContext"; /** * Returns a function which lazy loads a nested module @@ -27,7 +27,7 @@ import {Resolvable} from "../resolve/resolvable"; export function loadNgModule(path: string): (transition: Transition) => Promise { /** Get the parent NgModule Injector (from resolves) */ const getNg2Injector = (transition: Transition) => - transition.injector().getAsync(NG2_INJECTOR_TOKEN); + transition.injector().getAsync(NATIVE_INJECTOR_TOKEN); /** * Lazy loads the NgModule using the NgModuleFactoryLoader @@ -70,8 +70,8 @@ export function loadNgModule(path: string): (transition: Transition) => Promise< throw new Error('Lazy loaded modules should not contain a UIRouterModule.forRoot() module'); } - let childModules: ChildModule[] = injector.get(UIROUTER_CHILD_MODULE); - childModules.forEach(module => applyModuleConfig(uiRouter, injector, module)); + let modules: StatesModule[] = injector.get(UIROUTER_MODULE_TOKEN); + modules.forEach(module => applyModuleConfig(uiRouter, injector, module)); let replacementState = uiRouter.stateRegistry.get(originalName); if (replacementState === originalState) { @@ -79,7 +79,7 @@ export function loadNgModule(path: string): (transition: Transition) => Promise< } // Supply the newly loaded states with the Injector from the lazy loaded NgModule - replacementState.$$state().resolvables.push(Resolvable.fromData(NG2_INJECTOR_TOKEN, injector)); + replacementState.$$state().resolvables.push(Resolvable.fromData(NATIVE_INJECTOR_TOKEN, injector)); return {}; } diff --git a/src/ng2/providers.ts b/src/ng2/providers.ts index d4697dbd7..460af25e0 100644 --- a/src/ng2/providers.ts +++ b/src/ng2/providers.ts @@ -95,14 +95,15 @@ import {UrlRouter} from "../url/urlRouter"; import {ViewService} from "../view/view"; import {UIView, ParentUIViewInject} from "./directives/uiView"; import {ng2ViewsBuilder, Ng2ViewConfig} from "./statebuilders/views"; -import {Ng2ViewDeclaration, NG2_INJECTOR_TOKEN} from "./interface"; +import {Ng2ViewDeclaration} from "./interface"; import {applyRootModuleConfig, applyModuleConfig} from "./uiRouterConfig"; import {Globals} from "../globals"; import {UIRouterLocation} from "./location"; import {services} from "../common/coreservices"; import {Resolvable} from "../resolve/resolvable"; -import {RootModule, ChildModule, UIROUTER_ROOT_MODULE, UIROUTER_CHILD_MODULE} from "./uiRouterNgModule"; +import {RootModule, StatesModule, UIROUTER_ROOT_MODULE, UIROUTER_MODULE_TOKEN} from "./uiRouterNgModule"; import {UIRouterRx} from "./rx"; +import {NATIVE_INJECTOR_TOKEN} from "../resolve/resolveContext"; /** * This is a factory function for a UIRouter instance @@ -115,7 +116,7 @@ let uiRouterFactory = ( injector: Injector) => { let rootModules: RootModule[] = injector.get(UIROUTER_ROOT_MODULE); - let childModules: ChildModule[] = injector.get(UIROUTER_CHILD_MODULE); + let modules: StatesModule[] = injector.get(UIROUTER_MODULE_TOKEN); if (rootModules.length !== 1) { throw new Error("Exactly one UIRouterModule.forRoot() should be in the bootstrapped app module's imports: []"); @@ -144,7 +145,7 @@ let uiRouterFactory = ( registry.stateQueue.flush(router.stateService); // Prep the tree of NgModule by placing the root NgModule's Injector on the root state. - let ng2InjectorResolvable = Resolvable.fromData(NG2_INJECTOR_TOKEN, injector); + let ng2InjectorResolvable = Resolvable.fromData(NATIVE_INJECTOR_TOKEN, injector); registry.root().resolvables.push(ng2InjectorResolvable); @@ -154,7 +155,7 @@ let uiRouterFactory = ( setTimeout(() => { rootModules.forEach(moduleConfig => applyRootModuleConfig(router, injector, moduleConfig)); - childModules.forEach(moduleConfig => applyModuleConfig(router, injector, moduleConfig)); + modules.forEach(moduleConfig => applyModuleConfig(router, injector, moduleConfig)); // Start monitoring the URL if (!router.urlRouterProvider.interceptDeferred) { diff --git a/src/ng2/uiRouterConfig.ts b/src/ng2/uiRouterConfig.ts index a91049d38..018c8c401 100644 --- a/src/ng2/uiRouterConfig.ts +++ b/src/ng2/uiRouterConfig.ts @@ -1,10 +1,10 @@ /** @module ng2 */ /** */ import {UIRouter} from "../router"; -import {ChildModule, RootModule} from "./uiRouterNgModule"; +import {StatesModule, RootModule} from "./uiRouterNgModule"; import {Injector} from "@angular/core"; import {isDefined} from "../common/predicates"; -export function applyModuleConfig(uiRouter: UIRouter, injector: Injector, options: ChildModule) { +export function applyModuleConfig(uiRouter: UIRouter, injector: Injector, options: StatesModule) { if (options.configClass) { injector.get(options.configClass); } @@ -29,8 +29,6 @@ export function applyRootModuleConfig(uiRouter: UIRouter, injector: Injector, co uiRouter.urlRouterProvider.otherwise( config.otherwise); } } - - applyModuleConfig(uiRouter, injector, config); } diff --git a/src/ng2/uiRouterNgModule.ts b/src/ng2/uiRouterNgModule.ts index 5500e366e..c6960f614 100644 --- a/src/ng2/uiRouterNgModule.ts +++ b/src/ng2/uiRouterNgModule.ts @@ -110,31 +110,30 @@ export class UIRouterModule { * @param module UI-Router module options * @returns an `NgModule` */ - static forChild(module: ChildModule = {}): ModuleWithProviders { + static forChild(module: StatesModule = {}): ModuleWithProviders { return { ngModule: UIRouterModule, providers: UIRouterModule.makeProviders(module, false), } } - static makeProviders(module: ChildModule, forRoot: boolean): Provider[] { - let provideConfig: Provider[] = [module.configClass] + static makeProviders(module: StatesModule, forRoot: boolean): Provider[] { + let providers: Provider[] = [module.configClass] .filter(identity) .map(configClass => ({ provide: configClass, useClass: configClass })); - let UIROUTER_MODULE_TOKEN = forRoot ? UIROUTER_ROOT_MODULE : UIROUTER_CHILD_MODULE; + if (forRoot) providers.push({ provide: UIROUTER_ROOT_MODULE, useValue: module, multi: true}); + providers.push({ provide: UIROUTER_MODULE_TOKEN, useValue: module, multi: true }); + providers.push({ provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: module.states || [], multi: true }); - return provideConfig.concat([ - { provide: UIROUTER_MODULE_TOKEN, useValue: module, multi: true }, - { provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: module.states || [], multi: true }, - ]); + return providers; } } /** * UI-Router declarative configuration which can be provided to [[UIRouterModule.forRoot]] */ -export interface RootModule extends ChildModule { +export interface RootModule extends StatesModule { /** * Chooses a `LocationStrategy`. * @@ -162,7 +161,7 @@ export interface RootModule extends ChildModule { /** * UI-Router Module declarative configuration which can be passed to [[UIRouterModule.forChild]] */ -export interface ChildModule { +export interface StatesModule { /** * The module's UI-Router states * @@ -214,6 +213,6 @@ export interface ChildModule { configClass?: Type; } -export const UIROUTER_ROOT_MODULE = new OpaqueToken("UIRouter Module Root Options"); -export const UIROUTER_CHILD_MODULE = new OpaqueToken("UIRouter Module Options"); +export const UIROUTER_ROOT_MODULE = new OpaqueToken("UIRouter Root Module"); +export const UIROUTER_MODULE_TOKEN = new OpaqueToken("UIRouter Module"); diff --git a/src/resolve/resolveContext.ts b/src/resolve/resolveContext.ts index 7b5cd702a..c3590d238 100644 --- a/src/resolve/resolveContext.ts +++ b/src/resolve/resolveContext.ts @@ -2,7 +2,7 @@ import { find, tail, uniqR, unnestR, inArray } from "../common/common"; import {propEq} from "../common/hof"; import {trace} from "../common/trace"; -import {services} from "../common/coreservices"; +import {services, $InjectorLike} from "../common/coreservices"; import {resolvePolicies, PolicyWhen} from "./interface"; import {PathNode} from "../path/node"; @@ -13,10 +13,12 @@ import {stringify} from "../common/strings"; import {Transition} from "../transition/transition"; import {UIInjector} from "../common/interface"; -var when = resolvePolicies.when; +const when = resolvePolicies.when; const ALL_WHENS = [when.EAGER, when.LAZY]; const EAGER_WHENS = [when.EAGER]; +export const NATIVE_INJECTOR_TOKEN = "Native Injector"; + /** * Encapsulates Depenency Injection for a path of nodes * @@ -28,6 +30,7 @@ const EAGER_WHENS = [when.EAGER]; * The ResolveContext closes over the [[PathNode]]s, and provides DI for the last node in the path. */ export class ResolveContext { + _injector: UIInjector; constructor(private _path: PathNode[]) { } @@ -131,7 +134,7 @@ export class ResolveContext { } injector(): UIInjector { - return new UIInjectorImpl(this); + return this._injector || (this._injector = new UIInjectorImpl(this)); } findNode(resolvable: Resolvable): PathNode { @@ -147,8 +150,8 @@ export class ResolveContext { let node = this.findNode(resolvable); // Find which other resolvables are "visible" to the `resolvable` argument // subpath stopping at resolvable's node, or the whole path (if the resolvable isn't in the path) - var subPath: PathNode[] = PathFactory.subPath(this._path, x => x === node) || this._path; - var availableResolvables: Resolvable[] = subPath + let subPath: PathNode[] = PathFactory.subPath(this._path, x => x === node) || this._path; + let availableResolvables: Resolvable[] = subPath .reduce((acc, node) => acc.concat(node.resolvables), []) //all of subpath's resolvables .filter(res => res !== resolvable); // filter out the `resolvable` argument @@ -156,7 +159,7 @@ export class ResolveContext { let matching = availableResolvables.filter(r => r.token === token); if (matching.length) return tail(matching); - let fromInjector = services.$injector.get(token); + let fromInjector = this.injector().get(token); if (!fromInjector) { throw new Error("Could not find Dependency Injection token: " + stringify(token)); } @@ -169,7 +172,12 @@ export class ResolveContext { } class UIInjectorImpl implements UIInjector { - constructor(public context: ResolveContext) { } + native: $InjectorLike; + + constructor(public context: ResolveContext) { + this.native = this.get(NATIVE_INJECTOR_TOKEN) || services.$injector; + } + get(token: any) { var resolvable = this.context.getResolvable(token); if (resolvable) { @@ -178,15 +186,12 @@ class UIInjectorImpl implements UIInjector { } return resolvable.data; } - return services.$injector.get(token); + return this.native && this.native.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)); + return services.$q.when(this.native.get(token)); } - - /** The native injector ($injector on ng1, Root Injector on ng2, justjs injector for everything else) */ - native = services.$injector; } \ No newline at end of file