Skip to content

Commit c518510

Browse files
refactor(PathElement): refactor resolve policy code so it's readable/comprehensible
1 parent 802119e commit c518510

File tree

3 files changed

+81
-42
lines changed

3 files changed

+81
-42
lines changed

src/resolve/interface.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Resolvable from "./resolvable";
2+
import {IPromise} from "angular"
3+
4+
export interface IResolvables {
5+
[key:string]: Resolvable
6+
}
7+
8+
export interface IPromises {
9+
[key:string]: IPromise<any>
10+
}

src/resolve/path.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {IState} from "../state/interface";
88
import {runtime} from "../common/angular1"
99
import PathElement from "./pathElement";
1010
import Resolvable from "./resolvable";
11+
import {IResolvables} from "./interface";
1112

1213

1314
/**
@@ -67,7 +68,7 @@ export default class Path {
6768
* state({ name: 'G.G2', resolve: { _G: function(_G) { return _G + "G2"; } } });
6869
* where injecting _G into a controller will yield "GG2"
6970
*/
70-
getResolvables(options?: any): { [key:string]:Resolvable; } {
71+
getResolvables(options?: any): IResolvables {
7172
options = defaults(options, { omitOwnLocals: [] });
7273
var last = this.last();
7374
return this.elements.reduce(function(memo, elem) {

src/resolve/pathElement.ts

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,48 @@
11
/// <reference path='../../typings/angularjs/angular.d.ts' />
22

3-
import {isObject, isString, extend, forEach, noop, pick, map, filter, parse} from "../common/common";
3+
import {isObject, isString, extend, forEach, noop, prop, pick, map, filter, parse} from "../common/common";
44
import trace from "../common/trace";
55
import {IPromise} from "angular";
66
import {IState} from "../state/interface";
77
import Path from "./path";
88
import Resolvable from "./resolvable";
9-
import {runtime} from "../common/angular1"
10-
11-
12-
// Eager resolves are resolved before the transition starts.
13-
// Lazy resolves are resolved before their state is entered.
14-
// JIT resolves are resolved just-in-time, right before an injected function that depends on them is invoked.
15-
var resolvePolicies = { eager: 3, lazy: 2, jit: 1 };
9+
import {IResolvables, IPromises} from "./interface";
10+
import {runtime} from "../common/angular1";
11+
12+
interface IOrdinals { [key: string]: number };
13+
interface IPolicies { [key: string]: string };
14+
// TODO: convert to enum
15+
// Defines the available policies and their ordinals.
16+
const resolvePolicies: IOrdinals = {
17+
eager: 3, // Eager resolves are resolved before the transition starts.
18+
lazy: 2, // Lazy resolves are resolved before their state is entered.
19+
jit: 1 // JIT resolves are resolved just-in-time, right before an injected function that depends on them is invoked.
20+
};
1621
var defaultResolvePolicy = "jit"; // TODO: make this configurable
1722

23+
/**
24+
* Given a state's resolvePolicy attribute and map of resolvables, returns the policy ordinal for each resolvable
25+
* Use the policy declared for the Resolve. If undefined, use the policy declared for the State. If
26+
* undefined, use the system defaultResolvePolicy.
27+
*
28+
* @param stateResolvePolicyConf The raw resolvePolicy declaration on the state object; may be a String or Object
29+
* @param resolvables The resolvables to fetch resolve policies for
30+
*/
31+
function getResolvablesPolicies(stateResolvePolicyConf, resolvables: IResolvables): IOrdinals {
32+
// Normalize the configuration on the state to either state-level (a string) or resolve-level (a Map of string:string)
33+
let stateLevelPolicy: string = <string> (isString(stateResolvePolicyConf) ? stateResolvePolicyConf : null);
34+
let resolveLevelPolicies: IPolicies = <any> (isObject(stateResolvePolicyConf) ? stateResolvePolicyConf : {});
35+
36+
const getOrdinalFor = (stateLevelPolicy, resolveLevelPolicies, resolvable) => {
37+
let policy = resolveLevelPolicies[resolvable.name] || stateLevelPolicy || defaultResolvePolicy;
38+
return resolvePolicies[policy];
39+
}
40+
41+
// Determine each resolvable's Resolve Policy (as an ordinal).
42+
const toPolicyOrdinal = resolvable => getOrdinalFor(stateLevelPolicy, resolveLevelPolicies, resolvable)
43+
return <any> map(resolvables, toPolicyOrdinal);
44+
}
45+
1846
/**
1947
* An element in the path which represents a state and that state's Resolvables and their resolve statuses.
2048
* When the resolved data is ready, it is stored in each Resolvable object within the PathElement
@@ -26,9 +54,8 @@ export default class PathElement {
2654
constructor(state: IState) {
2755
this.state = state;
2856
// Convert state's resolvable assoc-array into an assoc-array of empty Resolvable(s)
29-
this._resolvables = map(state.resolve || {}, function(resolveFn, resolveName) {
30-
return new Resolvable(resolveName, resolveFn, state);
31-
});
57+
const makeResolvable = (resolveFn, resolveName) => new Resolvable(resolveName, resolveFn, state);
58+
this._resolvables = map(state.resolve || {}, makeResolvable);
3259
}
3360

3461
state: IState;
@@ -42,31 +69,28 @@ export default class PathElement {
4269
return extend(this._resolvables, resolvablesByName);
4370
}
4471

45-
// returns a promise for all resolvables on this PathElement
46-
// options.policy: only return promises for those Resolvables which are at the specified policy strictness, or above.
72+
// returns a promise for all the resolvables on this PathElement
73+
// options.resolvePolicy: only return promises for those Resolvables which are at
74+
// the specified policy, or above. i.e., options.resolvePolicy === 'lazy' will
75+
// resolve both 'lazy' and 'eager' resolves.
4776
resolvePathElement(pathContext, options): IPromise<any> {
4877
options = options || {};
49-
var policyOrdinal = resolvePolicies[options && options.resolvePolicy || defaultResolvePolicy];
50-
51-
var policyConf = {
52-
$$state: isString(this.state.resolvePolicy) ? this.state.resolvePolicy : defaultResolvePolicy,
53-
$$resolves: isObject(this.state.resolvePolicy) ? this.state.resolvePolicy : defaultResolvePolicy
54-
};
55-
56-
// Isolate only this element's resolvables
57-
var elements: PathElement[] = [this];
58-
var resolvables = <any> (new Path(elements).getResolvables());
59-
forEach(resolvables, function(resolvable) {
60-
var policyString = policyConf.$$resolves[resolvable.name] || policyConf.$$state;
61-
policyConf[resolvable.name] = resolvePolicies[policyString];
62-
});
78+
// The caller can request the path be resolved for a given policy and "below"
79+
let policyOrdinal = resolvePolicies[options && options.resolvePolicy || defaultResolvePolicy];
80+
81+
// Get this path element's resolvables
82+
let resolvables: IResolvables = <any> (new Path([this]).getResolvables());
83+
// Get each resolvable's resolve policy
84+
let policies = getResolvablesPolicies(this.state.resolvePolicy, resolvables);
85+
86+
const matchesRequestedPolicy = resolvable => policies[resolvable.name] >= policyOrdinal;
87+
let matchingResolves = filter(resolvables, matchesRequestedPolicy);
6388

64-
const matchesPolicy = (resolvable) => policyConf[resolvable.name] >= policyOrdinal;
6589
const getResolvePromise = (resolvable) => resolvable.get(pathContext, options);
90+
let resolvablePromises: IPromises = <any> map(matchingResolves, getResolvePromise);
6691

67-
var matchingResolves = filter(resolvables, matchesPolicy);
6892
if (options.trace) trace.traceResolvePathElement(this, matchingResolves, options);
69-
var resolvablePromises = map(matchingResolves, getResolvePromise);
93+
7094
return runtime.$q.all(resolvablePromises).then(noop);
7195
}
7296

@@ -79,11 +103,10 @@ export default class PathElement {
79103
// pathContext is a Path which is used to retrieve dependent Resolvables for injecting
80104
invokeLater(fn, locals, pathContext, options): IPromise<any> {
81105
options = options || {};
82-
var deps = runtime.$injector.annotate(fn);
83-
var resolvables = pick(pathContext.pathFromRoot(this).getResolvables(), deps);
84-
if (options.trace) trace.tracePathElementInvoke(this, fn, deps, extend({ when: "Later"}, options));
85-
86-
var promises: any = map(resolvables, function(resolvable) { return resolvable.get(pathContext, options); });
106+
var resolvables = this._resolvablesForFn(fn, pathContext, options, "Later");
107+
var getPromise = resolvable => resolvable.get(pathContext, options);
108+
var promises: any = map(resolvables, getPromise);
109+
87110
return runtime.$q.all(promises).then(() => {
88111
try {
89112
return this.invokeNow(fn, locals, pathContext, options);
@@ -99,15 +122,20 @@ export default class PathElement {
99122
// Injects a function at this PathElement level with available Resolvables
100123
// Does not wait until all Resolvables have been resolved; you must call PathElement.resolve() (or manually resolve each dep) first
101124
invokeNow(fn: Function, locals: any, pathContext: Path, options: any = {}) {
102-
var deps = runtime.$injector.annotate(fn);
103-
var resolvables = pick(pathContext.pathFromRoot(this).getResolvables(), deps);
104-
if (options.trace) trace.tracePathElementInvoke(this, fn, runtime.$injector.annotate(fn), extend({ when: "Now "}, options));
105-
106-
var moreLocals = map(resolvables, function(resolvable) { return resolvable.data; });
107-
var combinedLocals = extend({}, locals, moreLocals);
125+
var resolvables = this._resolvablesForFn(fn, pathContext, options, "Now ");
126+
var resolvedLocals = map(resolvables, prop("data"));
127+
var combinedLocals = extend({}, locals, resolvedLocals);
108128
return runtime.$injector.invoke(fn, this.state, combinedLocals);
109129
}
110130

131+
/** Inspects a function `fn` for its dependencies. Returns an object containing matching Resolvables */
132+
private _resolvablesForFn(fn: Function, pathContext: Path, options, when: string): {[key:string]: Resolvable} {
133+
var deps = runtime.$injector.annotate(fn);
134+
var resolvables = <any> pick(pathContext.pathFromRoot(this).getResolvables(), deps);
135+
if (options.trace) trace.tracePathElementInvoke(this, fn, deps, extend({when: when}, options));
136+
return resolvables;
137+
}
138+
111139
toString(): string {
112140
var state = parse("state.name")(this) || "(root)";
113141
return `PathElement(${state})`;

0 commit comments

Comments
 (0)