Skip to content

Commit

Permalink
fix(ng2.NgModule): Allow apps with no forChild modules
Browse files Browse the repository at this point in the history
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
  • Loading branch information
christopherthielen committed Sep 21, 2016
1 parent 88c6494 commit d3bd332
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 42 deletions.
5 changes: 2 additions & 3 deletions src/ng2/directives/uiView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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);
Expand Down
12 changes: 6 additions & 6 deletions src/ng2/lazyLoadNgModule.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -27,7 +27,7 @@ import {Resolvable} from "../resolve/resolvable";
export function loadNgModule(path: string): (transition: Transition) => Promise<LazyLoadResult> {
/** 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
Expand Down Expand Up @@ -70,16 +70,16 @@ 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) {
throw new Error(`The module that was loaded from ${path} should have a ui-router state named '${originalName}'`);
}

// 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 {};
}
Expand Down
11 changes: 6 additions & 5 deletions src/ng2/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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: []");
Expand Down Expand Up @@ -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);


Expand All @@ -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) {
Expand Down
6 changes: 2 additions & 4 deletions src/ng2/uiRouterConfig.ts
Original file line number Diff line number Diff line change
@@ -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);
}
Expand All @@ -29,8 +29,6 @@ export function applyRootModuleConfig(uiRouter: UIRouter, injector: Injector, co
uiRouter.urlRouterProvider.otherwise(<any> config.otherwise);
}
}

applyModuleConfig(uiRouter, injector, config);
}


23 changes: 11 additions & 12 deletions src/ng2/uiRouterNgModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
*
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -214,6 +213,6 @@ export interface ChildModule {
configClass?: Type<any>;
}

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

29 changes: 17 additions & 12 deletions src/resolve/resolveContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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
*
Expand All @@ -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[]) { }

Expand Down Expand Up @@ -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 {
Expand All @@ -147,16 +150,16 @@ 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

const getDependency = (token: any) => {
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));
}
Expand All @@ -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) {
Expand All @@ -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;
}

0 comments on commit d3bd332

Please sign in to comment.