Skip to content

Commit e9f26f8

Browse files
BC-BREAK: Replaced ViewConfig.node with ViewConfig.path. el.data('$uiView') is affected. Previously the .node was the node for the view. Now the last element in the path is the node for the view.
BC-BREAK: Nodes no longer have (stateful) `.resolveContext` properties. Instead, a new ResolveContext is wrapped over a Path of Nodes. Removed `PathFactory.bindResolveContexts()`. BC-BREAK: ResolveContext.resolvePath returns a promise for resolved data as an array of tuples, instead of a promise for an object of resolved data. Removed `ResolveContext.resolvePathElement()`.
1 parent 9cafc06 commit e9f26f8

22 files changed

+117
-174
lines changed

src/ng1/directives/stateDirectives.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
*
66
* @preferred @module ng1_directives
77
*/ /** for typedoc */
8-
import {extend, forEach, toJson} from "../../common/common";
8+
import {extend, forEach, toJson, tail} from "../../common/common";
99
import {isString, isObject} from "../../common/predicates";
1010
import {UIViewData} from "./viewDirective";
1111
import {parse} from "../../common/hof";
12+
import {PathNode} from "../../path/node";
1213

1314
/** @hidden */
1415
function parseStateRef(ref, current) {
@@ -22,8 +23,8 @@ function parseStateRef(ref, current) {
2223
/** @hidden */
2324
function stateContext(el) {
2425
let $uiView: UIViewData = el.parent().inheritedData('$uiView');
25-
let context = parse('$cfg.node.state')($uiView);
26-
return context && context.name ? context : undefined;
26+
let path: PathNode[] = parse('$cfg.path')($uiView);
27+
return path ? tail(path).state.name : undefined;
2728
}
2829

2930
/** @hidden */

src/ng1/directives/viewDirective.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @module ng1_directives */ /** for typedoc */
22
"use strict";
3-
import {extend, unnestR, filter} from "../../common/common";
3+
import {extend, unnestR, filter, tail} from "../../common/common";
44
import {isDefined, isFunction, isString} from "../../common/predicates";
55
import {trace} from "../../common/trace";
66
import {ActiveUIView} from "../../view/interface";
@@ -13,7 +13,7 @@ import {PathNode} from "../../path/node";
1313
import {Param} from "../../params/param";
1414
import {kebobString} from "../../common/strings";
1515
import {HookRegOptions} from "../../transition/interface";
16-
import {Ng1Controller} from "../interface";
16+
import {Ng1Controller, Ng1StateDeclaration} from "../interface";
1717
import {getLocals} from "../services";
1818

1919
/** @hidden */
@@ -315,7 +315,6 @@ $ViewDirectiveFill.$inject = ['$compile', '$controller', '$transitions', '$view'
315315
function $ViewDirectiveFill ( $compile, $controller, $transitions, $view, $timeout) {
316316
const getControllerAs = parse('viewDecl.controllerAs');
317317
const getResolveAs = parse('viewDecl.resolveAs');
318-
const getResolveContext = parse('node.resolveContext');
319318

320319
return {
321320
restrict: 'ECA',
@@ -335,8 +334,7 @@ function $ViewDirectiveFill ( $compile, $controller, $transitions, $view,
335334
let controller = cfg.controller;
336335
let controllerAs: string = getControllerAs(cfg);
337336
let resolveAs: string = getResolveAs(cfg);
338-
let resolveCtx: ResolveContext = getResolveContext(cfg);
339-
337+
let resolveCtx: ResolveContext = cfg.path && new ResolveContext(cfg.path);
340338
let locals = resolveCtx && getLocals(resolveCtx);
341339

342340
scope[resolveAs] = locals;
@@ -390,16 +388,19 @@ function registerControllerCallbacks($transitions: TransitionService, controller
390388
// Call $onInit() ASAP
391389
if (isFunction(controllerInstance.$onInit) && !(cfg.viewDecl.component && hasComponentImpl)) controllerInstance.$onInit();
392390

391+
let viewState: Ng1StateDeclaration = tail(cfg.path).state.self;
392+
393393
var hookOptions: HookRegOptions = { bind: controllerInstance };
394394
// Add component-level hook for onParamsChange
395395
if (isFunction(controllerInstance.uiOnParamsChanged)) {
396+
let resolveContext: ResolveContext = new ResolveContext(cfg.path);
397+
let viewCreationTrans = resolveContext.getResolvable('$transition$').data;
398+
396399
// Fire callback on any successful transition
397400
const paramsUpdated = ($transition$: Transition) => {
398-
let ctx: ResolveContext = cfg.node.resolveContext;
399-
let viewCreationTrans = ctx.getResolvable('$transition$').data;
400401
// Exit early if the $transition$ is the same as the view was created within.
401402
// Exit early if the $transition$ will exit the state the view is for.
402-
if ($transition$ === viewCreationTrans || $transition$.exiting().indexOf(cfg.node.state.self) !== -1) return;
403+
if ($transition$ === viewCreationTrans || $transition$.exiting().indexOf(viewState) !== -1) return;
403404

404405
let toParams = $transition$.params("to");
405406
let fromParams = $transition$.params("from");
@@ -424,7 +425,7 @@ function registerControllerCallbacks($transitions: TransitionService, controller
424425

425426
// Add component-level hook for uiCanExit
426427
if (isFunction(controllerInstance.uiCanExit)) {
427-
var criteria = {exiting: cfg.node.state.name};
428+
var criteria = {exiting: viewState.name};
428429
$scope.$on('$destroy', $transitions.onBefore(criteria, controllerInstance.uiCanExit, hookOptions));
429430
}
430431
}

src/ng1/legacy/resolveService.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ export const resolveFactory = () => ({
2222
const rewrap = _locals => resolvablesBuilder(<any> { resolve: map(_locals, local => () => local) });
2323
context.addResolvables(rewrap(parentLocals), parentNode.state);
2424
context.addResolvables(rewrap(locals), node.state);
25-
return context.resolvePath();
25+
26+
const tuples2ObjR = (acc, tuple) => {
27+
acc[tuple.token] = tuple.value;
28+
return acc;
29+
};
30+
return context.resolvePath().then(results => results.reduce(tuples2ObjR, {}));
2631
};
2732

2833
return parent ? parent.then(resolveData) : resolveData({});

src/ng1/statebuilders/onEnterExitRetain.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {Transition} from "../../transition/transition";
55
import IInjectorService = angular.auto.IInjectorService;
66
import {services} from "../../common/coreservices";
77
import {getLocals} from "../services";
8-
import {tail} from "../../common/common";
8+
import {ResolveContext} from "../../resolve/resolveContext";
99

1010
/**
1111
* This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`,
@@ -18,8 +18,8 @@ export const getStateHookBuilder = (hookName) =>
1818
function stateHookBuilder(state: State, parentFn): TransitionStateHookFn {
1919
let hook = state[hookName];
2020
function decoratedNg1Hook(trans: Transition, inj: IInjectorService): HookResult {
21-
let locals = getLocals(tail(trans.treeChanges().to).resolveContext);
22-
return services.$injector.invoke(hook, this, locals);
21+
let resolveContext = new ResolveContext(trans.treeChanges().to);
22+
return services.$injector.invoke(hook, this, getLocals(resolveContext));
2323
}
2424

2525
return hook ? decoratedNg1Hook : undefined;

src/ng1/statebuilders/views.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @module ng1 */ /** */
22
import {State} from "../../state/stateObject";
3-
import {pick, forEach, anyTrueR, unnestR, tail} from "../../common/common";
3+
import {pick, forEach, anyTrueR, unnestR, tail, extend} from "../../common/common";
44
import {kebobString} from "../../common/strings";
55
import {ViewConfig} from "../../view/interface";
66
import {Ng1ViewDeclaration} from "../interface";
@@ -13,7 +13,7 @@ import {TemplateFactory} from "../templateFactory";
1313
import {ResolveContext} from "../../resolve/resolveContext";
1414
import {Resolvable} from "../../resolve/resolvable";
1515

16-
export const ng1ViewConfigFactory = (node, view) => new Ng1ViewConfig(node, view);
16+
export const ng1ViewConfigFactory = (path, view) => new Ng1ViewConfig(path, view);
1717

1818
/**
1919
* This is a [[StateBuilder.builder]] function for angular1 `views`.
@@ -105,15 +105,16 @@ export class Ng1ViewConfig implements ViewConfig {
105105
template: string;
106106
locals: any; // TODO: delete me
107107

108-
constructor(public node: PathNode, public viewDecl: Ng1ViewDeclaration) { }
108+
constructor(public path: PathNode[], public viewDecl: Ng1ViewDeclaration) { }
109109

110110
load() {
111111
let $q = services.$q;
112112
if (!this.hasTemplate())
113113
throw new Error(`No template configuration specified for '${this.viewDecl.$uiViewName}@${this.viewDecl.$uiViewContextAnchor}'`);
114114

115-
let context = this.node.resolveContext;
116-
let params = this.node.paramValues;
115+
let context = new ResolveContext(this.path);
116+
let params = this.path.reduce((acc, node) => extend(acc, node.paramValues), {});
117+
117118
let promises: any = {
118119
template: $q.when(this.getTemplate(params, new TemplateFactory(), context)),
119120
controller: $q.when(this.getController(context))

src/ng2/directives/uiView.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {Inject} from "@angular/core";
1414
import {ViewContext, ViewConfig} from "../../view/interface";
1515
import {Ng2ViewDeclaration} from "../interface";
1616
import {Ng2ViewConfig} from "../statebuilders/views";
17+
import {ResolveContext} from "../../resolve/resolveContext";
1718

1819
/** @hidden */
1920
let id = 0;
@@ -180,7 +181,7 @@ export class UiView {
180181
if (!config) return;
181182

182183
// Map resolves to "useValue providers"
183-
let context = config.node.resolveContext;
184+
let context = new ResolveContext(config.path);
184185
let resolvables = context.getTokens().map(token => context.getResolvable(token)).filter(r => r.resolved);
185186
let rawProviders = resolvables.map(r => ({ provide: r.token, useValue: r.data }));
186187
rawProviders.push(provide(UiView.PARENT_INJECT, { useValue: { context: config.viewDecl.$context, fqn: uiViewData.fqn } }));

src/ng2/providers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ let uiRouterFactory = (routerConfig: UIRouterConfig, location: UIRouterLocation,
6969

7070
location.init();
7171

72-
router.viewService.viewConfigFactory("ng2", (node: PathNode, config: Ng2ViewDeclaration) => new Ng2ViewConfig(node, config));
72+
router.viewService.viewConfigFactory("ng2", (path: PathNode[], config: Ng2ViewDeclaration) => new Ng2ViewConfig(path, config));
7373
router.stateRegistry.decorator('views', ng2ViewsBuilder);
7474

7575
router.stateRegistry.stateQueue.autoFlush(router.stateService);

src/ng2/statebuilders/views.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function ng2ViewsBuilder(state: State) {
3939
export class Ng2ViewConfig implements ViewConfig {
4040
loaded: boolean = true;
4141

42-
constructor(public node: PathNode, public viewDecl: Ng2ViewDeclaration) { }
42+
constructor(public path: PathNode[], public viewDecl: Ng2ViewDeclaration) { }
4343

4444
load() {
4545
return services.$q.when(this);

src/path/node.ts

-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {State} from "../state/stateObject";
55
import {RawParams} from "../params/interface";
66
import {Param} from "../params/param";
77
import {Resolvable} from "../resolve/resolvable";
8-
import {ResolveContext} from "../resolve/resolveContext";
98
import {ViewConfig} from "../view/interface";
109

1110
/**
@@ -22,8 +21,6 @@ export class PathNode {
2221
public paramSchema: Param[];
2322
/** The parameter values that belong to the state */
2423
public paramValues: { [key: string]: any };
25-
/** A context object used in conjunction with [[resolvables]] to manage resolves */
26-
public resolveContext: ResolveContext;
2724
/** The individual (stateful) resolvable objects that belong to the state */
2825
public resolvables: Resolvable[];
2926
/** The state's declared view configuration objects */
@@ -41,7 +38,6 @@ export class PathNode {
4138
this.paramValues = extend({}, node.paramValues);
4239
this.resolvables = node.resolvables.slice();
4340
this.views = node.views && node.views.slice();
44-
this.resolveContext = node.resolveContext;
4541
} else {
4642
this.state = state;
4743
this.paramSchema = state.parameters({ inherit: false });

src/path/pathFactory.ts

+2-13
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ export class PathFactory {
4848
*/
4949
static applyViewConfigs($view: ViewService, path: PathNode[]) {
5050
return path.map(node => {
51+
let subPath = PathFactory.subPath(path, n => n === node);
5152
let viewDecls: _ViewDeclaration[] = values(node.state.views || {});
52-
let viewConfigs: ViewConfig[][] = viewDecls.map(view => $view.createViewConfig(node, view));
53+
let viewConfigs: ViewConfig[][] = viewDecls.map(view => $view.createViewConfig(subPath, view));
5354
node.views = viewConfigs.reduce(unnestR, []);
5455
return node;
5556
});
@@ -92,18 +93,6 @@ export class PathFactory {
9293
return <PathNode[]> toPath.map(makeInheritedParamsNode);
9394
}
9495

95-
/**
96-
* Given a path of nodes, creates and binds a ResolveContext to each node.
97-
* The ResolveContext is used to inject functions from the proper scoping in the resolve tree.
98-
*/
99-
static bindResolveContexts(resolvePath: PathNode[]): PathNode[] {
100-
let resolveContext = new ResolveContext(resolvePath);
101-
return resolvePath.map((node: PathNode) => {
102-
node.resolveContext = resolveContext.isolateRootTo(node.state);
103-
return node;
104-
});
105-
}
106-
10796
/**
10897
* Computes the tree changes (entering, exiting) between a fromPath and toPath.
10998
*/

src/resolve/resolveContext.ts

+30-41
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {Resolvables, ResolvePolicy, IOptions1} from "./interface";
1111
import {PathNode} from "../path/node";
1212
import {Resolvable} from "./resolvable";
1313
import {State} from "../state/stateObject";
14-
import {mergeR} from "../common/common";
1514
import {PathFactory} from "../path/pathFactory";
1615
import {stringify} from "../common/strings";
1716

@@ -23,19 +22,7 @@ interface Promises { [key: string]: Promise<any>; }
2322

2423
export class ResolveContext {
2524

26-
private _nodeFor: (s: State) => PathNode;
27-
private _pathTo: (s: State) => PathNode[];
28-
29-
constructor(private _path: PathNode[]) {
30-
extend(this, {
31-
_nodeFor(state: State): PathNode {
32-
return <PathNode> find(this._path, propEq('state', state));
33-
},
34-
_pathTo(state: State): PathNode[] {
35-
return PathFactory.subPath(this._path, node => node.state === state);
36-
}
37-
});
38-
}
25+
constructor(private _path: PathNode[]) { }
3926

4027
/** Gets all the tokens found in the resolve context, de-duplicated */
4128
getTokens() {
@@ -55,10 +42,12 @@ export class ResolveContext {
5542
return tail(matching);
5643
}
5744

58-
/** Inspects a function `fn` for its dependencies. Returns an object containing any matching Resolvables */
59-
getResolvablesForFn(fn: IInjectable): Resolvable[] {
60-
let deps = services.$injector.annotate(<Function> fn, services.$injector.strictDi);
61-
return deps.map(token => this.getResolvable(token));
45+
private _nodeFor(state: State): PathNode {
46+
return <PathNode> find(this._path, propEq('state', state));
47+
}
48+
49+
private _pathTo(state: State): PathNode[] {
50+
return PathFactory.subPath(this._path, node => node.state === state);
6251
}
6352

6453
isolateRootTo(state: State): ResolveContext {
@@ -78,34 +67,27 @@ export class ResolveContext {
7867
}
7968

8069
// Returns a promise for an array of resolved path Element promises
81-
resolvePath(options: IOptions1 = {}): Promise<any> {
82-
trace.traceResolvePath(this._path, options);
83-
const promiseForNode = (node: PathNode) => this.resolvePathElement(node.state, options);
84-
return services.$q.all(<any> map(this._path, promiseForNode)).then(all => all.reduce(mergeR, {}));
85-
}
86-
87-
// returns a promise for all the resolvables on this PathElement
88-
// options.resolvePolicy: only return promises for those Resolvables which are at
89-
// the specified policy, or above. i.e., options.resolvePolicy === 'lazy' will
90-
// resolve both 'lazy' and 'eager' resolves.
91-
resolvePathElement(state: State, options: IOptions1 = {}): Promise<any> {
92-
// The caller can request the path be resolved for a given policy and "below"
70+
resolvePath(options: IOptions1 = {}): Promise<{ token: any, value: any }[]> {
9371
let policy: string = options && options.resolvePolicy;
9472
let policyOrdinal: number = ResolvePolicy[policy || defaultResolvePolicy];
95-
// Get path Resolvables available to this element
96-
let resolvables = this.getOwnResolvables(state);
73+
// get the subpath to the state argument, if provided
74+
trace.traceResolvePath(this._path, options);
9775

98-
const matchesRequestedPolicy = resolvable => getPolicy(state.resolvePolicy, resolvable) >= policyOrdinal;
99-
let matchingResolves = filter(resolvables, matchesRequestedPolicy);
76+
let promises: Promise<any>[] = this._path.reduce((acc, node) => {
77+
const matchesRequestedPolicy = resolvable => getPolicy(node.state.resolvePolicy, resolvable) >= policyOrdinal;
78+
let nodeResolvables = node.resolvables.filter(matchesRequestedPolicy);
79+
let subContext = this.isolateRootTo(node.state);
10080

101-
const getResolvePromise = (resolvable: Resolvable) => resolvable.get(this.isolateRootTo(state), options);
102-
let resolvablePromises: Promises = <any> map(matchingResolves, getResolvePromise);
81+
// For the matching Resolvables, start their async fetch process.
82+
var getResult = (r: Resolvable) => r.get(subContext, options)
83+
// Return a tuple that includes the Resolvable's token
84+
.then(value => ({ token: r.token, value: value }));
85+
return acc.concat(nodeResolvables.map(getResult));
86+
}, []);
10387

104-
trace.traceResolvePathElement(this, matchingResolves, options);
88+
return services.$q.all(promises);
89+
}
10590

106-
return services.$q.all(resolvablePromises);
107-
}
108-
10991
injector(): { get(any): any } {
11092

11193
let get = (token: any) => {
@@ -117,6 +99,11 @@ export class ResolveContext {
11799
return { get };
118100
}
119101

102+
/**
103+
* Gets the async dependencies of a Resolvable
104+
*
105+
* Given a Resolvable, returns its dependencies as a Resolvable[]
106+
*/
120107
getDependencies(resolvable: Resolvable): Resolvable[] {
121108
// predicate that finds the node the resolvable belongs to
122109
const nodeForResolvable = node => node.resolvables.indexOf(resolvable) !== -1;
@@ -132,7 +119,9 @@ export class ResolveContext {
132119
if (matching.length) return tail(matching);
133120

134121
let fromInjector = services.$injector.get(token);
135-
if (!fromInjector) throw new Error("Could not find Dependency Injection token: " + stringify(token));
122+
if (!fromInjector) {
123+
throw new Error("Could not find Dependency Injection token: " + stringify(token));
124+
}
136125

137126
return new Resolvable(token, () => fromInjector, [], fromInjector);
138127
};

0 commit comments

Comments
 (0)