@angular-wave/angular.ts 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular-ts.esm.js +1 -1
- package/dist/angular-ts.umd.js +1 -1
- package/package.json +4 -1
- package/src/exts/messages.md +30 -30
- package/src/index.js +1 -0
- package/src/public.js +2 -0
- package/src/router/adapter/directives/stateDirectives.js +695 -0
- package/src/router/adapter/directives/viewDirective.js +514 -0
- package/src/router/adapter/injectables.js +314 -0
- package/src/router/adapter/interface.js +1 -0
- package/src/router/adapter/locationServices.js +84 -0
- package/src/router/adapter/services.js +126 -0
- package/src/router/adapter/stateFilters.js +43 -0
- package/src/router/adapter/stateProvider.js +137 -0
- package/src/router/adapter/statebuilders/onEnterExitRetain.js +30 -0
- package/src/router/adapter/statebuilders/views.js +146 -0
- package/src/router/adapter/templateFactory.js +218 -0
- package/src/router/adapter/viewScroll.js +31 -0
- package/src/router/core/common/common.js +496 -0
- package/src/router/core/common/coreservices.js +15 -0
- package/src/router/core/common/glob.js +75 -0
- package/src/router/core/common/hof.js +194 -0
- package/src/router/core/common/predicates.js +44 -0
- package/src/router/core/common/queue.js +41 -0
- package/src/router/core/common/safeConsole.js +38 -0
- package/src/router/core/common/strings.js +141 -0
- package/src/router/core/common/trace.js +232 -0
- package/src/router/core/globals.js +29 -0
- package/src/router/core/hooks/coreResolvables.js +33 -0
- package/src/router/core/hooks/ignoredTransition.js +25 -0
- package/src/router/core/hooks/invalidTransition.js +14 -0
- package/src/router/core/hooks/lazyLoad.js +102 -0
- package/src/router/core/hooks/onEnterExitRetain.js +55 -0
- package/src/router/core/hooks/redirectTo.js +36 -0
- package/src/router/core/hooks/resolve.js +57 -0
- package/src/router/core/hooks/updateGlobals.js +30 -0
- package/src/router/core/hooks/url.js +25 -0
- package/src/router/core/hooks/views.js +39 -0
- package/src/router/core/interface.js +3 -0
- package/src/router/core/params/README.md +8 -0
- package/src/router/core/params/param.js +232 -0
- package/src/router/core/params/paramType.js +139 -0
- package/src/router/core/params/paramTypes.js +163 -0
- package/src/router/core/params/stateParams.js +35 -0
- package/src/router/core/path/pathNode.js +77 -0
- package/src/router/core/path/pathUtils.js +199 -0
- package/src/router/core/resolve/interface.js +10 -0
- package/src/router/core/resolve/resolvable.js +124 -0
- package/src/router/core/resolve/resolveContext.js +211 -0
- package/src/router/core/router.js +203 -0
- package/src/router/core/state/README.md +21 -0
- package/src/router/core/state/stateBuilder.js +332 -0
- package/src/router/core/state/stateMatcher.js +65 -0
- package/src/router/core/state/stateObject.js +117 -0
- package/src/router/core/state/stateQueueManager.js +89 -0
- package/src/router/core/state/stateRegistry.js +175 -0
- package/src/router/core/state/stateService.js +592 -0
- package/src/router/core/state/targetState.js +159 -0
- package/src/router/core/transition/hookBuilder.js +127 -0
- package/src/router/core/transition/hookRegistry.js +175 -0
- package/src/router/core/transition/interface.js +14 -0
- package/src/router/core/transition/rejectFactory.js +122 -0
- package/src/router/core/transition/transition.js +739 -0
- package/src/router/core/transition/transitionEventType.js +27 -0
- package/src/router/core/transition/transitionHook.js +199 -0
- package/src/router/core/transition/transitionService.js +311 -0
- package/src/router/core/url/interface.js +1 -0
- package/src/router/core/url/urlConfig.js +165 -0
- package/src/router/core/url/urlMatcher.js +548 -0
- package/src/router/core/url/urlMatcherFactory.js +123 -0
- package/src/router/core/url/urlRouter.js +115 -0
- package/src/router/core/url/urlRule.js +202 -0
- package/src/router/core/url/urlRules.js +348 -0
- package/src/router/core/url/urlService.js +268 -0
- package/src/router/core/view/interface.js +1 -0
- package/src/router/core/view/view.js +312 -0
- package/src/router/router.js +58 -0
- package/test/module-test.html +6 -2
- package/test/module-test.js +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extend,
|
|
3
|
+
find,
|
|
4
|
+
pick,
|
|
5
|
+
omit,
|
|
6
|
+
tail,
|
|
7
|
+
mergeR,
|
|
8
|
+
unnestR,
|
|
9
|
+
inArray,
|
|
10
|
+
arrayTuples,
|
|
11
|
+
} from "../common/common";
|
|
12
|
+
import { prop, propEq } from "../common/hof";
|
|
13
|
+
import { TargetState } from "../state/targetState";
|
|
14
|
+
import { PathNode } from "./pathNode";
|
|
15
|
+
/**
|
|
16
|
+
* This class contains functions which convert TargetStates, Nodes and paths from one type to another.
|
|
17
|
+
*/
|
|
18
|
+
export class PathUtils {
|
|
19
|
+
/** Given a PathNode[], create an TargetState */
|
|
20
|
+
static makeTargetState(registry, path) {
|
|
21
|
+
const state = tail(path).state;
|
|
22
|
+
return new TargetState(
|
|
23
|
+
registry,
|
|
24
|
+
state,
|
|
25
|
+
path.map(prop("paramValues")).reduce(mergeR, {}),
|
|
26
|
+
{},
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
static buildPath(targetState) {
|
|
30
|
+
const toParams = targetState.params();
|
|
31
|
+
return targetState
|
|
32
|
+
.$state()
|
|
33
|
+
.path.map((state) => new PathNode(state).applyRawParams(toParams));
|
|
34
|
+
}
|
|
35
|
+
/** Given a fromPath: PathNode[] and a TargetState, builds a toPath: PathNode[] */
|
|
36
|
+
static buildToPath(fromPath, targetState) {
|
|
37
|
+
const toPath = PathUtils.buildPath(targetState);
|
|
38
|
+
if (targetState.options().inherit) {
|
|
39
|
+
return PathUtils.inheritParams(
|
|
40
|
+
fromPath,
|
|
41
|
+
toPath,
|
|
42
|
+
Object.keys(targetState.params()),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return toPath;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates ViewConfig objects and adds to nodes.
|
|
49
|
+
*
|
|
50
|
+
* On each [[PathNode]], creates ViewConfig objects from the views: property of the node's state
|
|
51
|
+
*/
|
|
52
|
+
static applyViewConfigs($view, path, states) {
|
|
53
|
+
// Only apply the viewConfigs to the nodes for the given states
|
|
54
|
+
path
|
|
55
|
+
.filter((node) => inArray(states, node.state))
|
|
56
|
+
.forEach((node) => {
|
|
57
|
+
const viewDecls = Object.values(node.state.views || {});
|
|
58
|
+
const subPath = PathUtils.subPath(path, (n) => n === node);
|
|
59
|
+
const viewConfigs = viewDecls.map((view) =>
|
|
60
|
+
$view.createViewConfig(subPath, view),
|
|
61
|
+
);
|
|
62
|
+
node.views = viewConfigs.reduce(unnestR, []);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Given a fromPath and a toPath, returns a new to path which inherits parameters from the fromPath
|
|
67
|
+
*
|
|
68
|
+
* For a parameter in a node to be inherited from the from path:
|
|
69
|
+
* - The toPath's node must have a matching node in the fromPath (by state).
|
|
70
|
+
* - The parameter name must not be found in the toKeys parameter array.
|
|
71
|
+
*
|
|
72
|
+
* Note: the keys provided in toKeys are intended to be those param keys explicitly specified by some
|
|
73
|
+
* caller, for instance, $state.transitionTo(..., toParams). If a key was found in toParams,
|
|
74
|
+
* it is not inherited from the fromPath.
|
|
75
|
+
*/
|
|
76
|
+
static inheritParams(fromPath, toPath, toKeys = []) {
|
|
77
|
+
function nodeParamVals(path, state) {
|
|
78
|
+
const node = find(path, propEq("state", state));
|
|
79
|
+
return extend({}, node && node.paramValues);
|
|
80
|
+
}
|
|
81
|
+
const noInherit = fromPath
|
|
82
|
+
.map((node) => node.paramSchema)
|
|
83
|
+
.reduce(unnestR, [])
|
|
84
|
+
.filter((param) => !param.inherit)
|
|
85
|
+
.map(prop("id"));
|
|
86
|
+
/**
|
|
87
|
+
* Given an [[PathNode]] "toNode", return a new [[PathNode]] with param values inherited from the
|
|
88
|
+
* matching node in fromPath. Only inherit keys that aren't found in "toKeys" from the node in "fromPath""
|
|
89
|
+
*/
|
|
90
|
+
function makeInheritedParamsNode(toNode) {
|
|
91
|
+
// All param values for the node (may include default key/vals, when key was not found in toParams)
|
|
92
|
+
let toParamVals = extend({}, toNode && toNode.paramValues);
|
|
93
|
+
// limited to only those keys found in toParams
|
|
94
|
+
const incomingParamVals = pick(toParamVals, toKeys);
|
|
95
|
+
toParamVals = omit(toParamVals, toKeys);
|
|
96
|
+
const fromParamVals = omit(
|
|
97
|
+
nodeParamVals(fromPath, toNode.state) || {},
|
|
98
|
+
noInherit,
|
|
99
|
+
);
|
|
100
|
+
// extend toParamVals with any fromParamVals, then override any of those those with incomingParamVals
|
|
101
|
+
const ownParamVals = extend(
|
|
102
|
+
toParamVals,
|
|
103
|
+
fromParamVals,
|
|
104
|
+
incomingParamVals,
|
|
105
|
+
);
|
|
106
|
+
return new PathNode(toNode.state).applyRawParams(ownParamVals);
|
|
107
|
+
}
|
|
108
|
+
// The param keys specified by the incoming toParams
|
|
109
|
+
return toPath.map(makeInheritedParamsNode);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Computes the tree changes (entering, exiting) between a fromPath and toPath.
|
|
113
|
+
*/
|
|
114
|
+
static treeChanges(fromPath, toPath, reloadState) {
|
|
115
|
+
const max = Math.min(fromPath.length, toPath.length);
|
|
116
|
+
let keep = 0;
|
|
117
|
+
const nodesMatch = (node1, node2) =>
|
|
118
|
+
node1.equals(node2, PathUtils.nonDynamicParams);
|
|
119
|
+
while (
|
|
120
|
+
keep < max &&
|
|
121
|
+
fromPath[keep].state !== reloadState &&
|
|
122
|
+
nodesMatch(fromPath[keep], toPath[keep])
|
|
123
|
+
) {
|
|
124
|
+
keep++;
|
|
125
|
+
}
|
|
126
|
+
/** Given a retained node, return a new node which uses the to node's param values */
|
|
127
|
+
function applyToParams(retainedNode, idx) {
|
|
128
|
+
const cloned = retainedNode.clone();
|
|
129
|
+
cloned.paramValues = toPath[idx].paramValues;
|
|
130
|
+
return cloned;
|
|
131
|
+
}
|
|
132
|
+
let from, retained, exiting, entering, to;
|
|
133
|
+
from = fromPath;
|
|
134
|
+
retained = from.slice(0, keep);
|
|
135
|
+
exiting = from.slice(keep);
|
|
136
|
+
// Create a new retained path (with shallow copies of nodes) which have the params of the toPath mapped
|
|
137
|
+
const retainedWithToParams = retained.map(applyToParams);
|
|
138
|
+
entering = toPath.slice(keep);
|
|
139
|
+
to = retainedWithToParams.concat(entering);
|
|
140
|
+
return { from, to, retained, retainedWithToParams, exiting, entering };
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Returns a new path which is: the subpath of the first path which matches the second path.
|
|
144
|
+
*
|
|
145
|
+
* The new path starts from root and contains any nodes that match the nodes in the second path.
|
|
146
|
+
* It stops before the first non-matching node.
|
|
147
|
+
*
|
|
148
|
+
* Nodes are compared using their state property and their parameter values.
|
|
149
|
+
* If a `paramsFn` is provided, only the [[Param]] returned by the function will be considered when comparing nodes.
|
|
150
|
+
*
|
|
151
|
+
* @param pathA the first path
|
|
152
|
+
* @param pathB the second path
|
|
153
|
+
* @param paramsFn a function which returns the parameters to consider when comparing
|
|
154
|
+
*
|
|
155
|
+
* @returns an array of PathNodes from the first path which match the nodes in the second path
|
|
156
|
+
*/
|
|
157
|
+
static matching(pathA, pathB, paramsFn) {
|
|
158
|
+
let done = false;
|
|
159
|
+
const tuples = arrayTuples(pathA, pathB);
|
|
160
|
+
return tuples.reduce((matching, [nodeA, nodeB]) => {
|
|
161
|
+
done = done || !nodeA.equals(nodeB, paramsFn);
|
|
162
|
+
return done ? matching : matching.concat(nodeA);
|
|
163
|
+
}, []);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Returns true if two paths are identical.
|
|
167
|
+
*
|
|
168
|
+
* @param pathA
|
|
169
|
+
* @param pathB
|
|
170
|
+
* @param paramsFn a function which returns the parameters to consider when comparing
|
|
171
|
+
* @returns true if the the states and parameter values for both paths are identical
|
|
172
|
+
*/
|
|
173
|
+
static equals(pathA, pathB, paramsFn) {
|
|
174
|
+
return (
|
|
175
|
+
pathA.length === pathB.length &&
|
|
176
|
+
PathUtils.matching(pathA, pathB, paramsFn).length === pathA.length
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Return a subpath of a path, which stops at the first matching node
|
|
181
|
+
*
|
|
182
|
+
* Given an array of nodes, returns a subset of the array starting from the first node,
|
|
183
|
+
* stopping when the first node matches the predicate.
|
|
184
|
+
*
|
|
185
|
+
* @param path a path of [[PathNode]]s
|
|
186
|
+
* @param predicate a [[Predicate]] fn that matches [[PathNode]]s
|
|
187
|
+
* @returns a subpath up to the matching node, or undefined if no match is found
|
|
188
|
+
*/
|
|
189
|
+
static subPath(path, predicate) {
|
|
190
|
+
const node = find(path, predicate);
|
|
191
|
+
const elementIdx = path.indexOf(node);
|
|
192
|
+
return elementIdx === -1 ? undefined : path.slice(0, elementIdx + 1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
PathUtils.nonDynamicParams = (node) =>
|
|
196
|
+
node.state.parameters({ inherit: false }).filter((param) => !param.dynamic);
|
|
197
|
+
/** Gets the raw parameter values from a path */
|
|
198
|
+
PathUtils.paramValues = (path) =>
|
|
199
|
+
path.reduce((acc, node) => extend(acc, node.paramValues), {});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { extend, identity } from "../../../core/utils";
|
|
2
|
+
import { services } from "../common/coreservices";
|
|
3
|
+
import { trace } from "../common/trace";
|
|
4
|
+
import { stringify } from "../common/strings";
|
|
5
|
+
import { isFunction, isObject } from "../common/predicates";
|
|
6
|
+
import { isNullOrUndefined } from "../common/predicates";
|
|
7
|
+
// TODO: explicitly make this user configurable
|
|
8
|
+
export let defaultResolvePolicy = {
|
|
9
|
+
when: "LAZY",
|
|
10
|
+
async: "WAIT",
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The basic building block for the resolve system.
|
|
14
|
+
*
|
|
15
|
+
* Resolvables encapsulate a state's resolve's resolveFn, the resolveFn's declared dependencies, the wrapped (.promise),
|
|
16
|
+
* and the unwrapped-when-complete (.data) result of the resolveFn.
|
|
17
|
+
*
|
|
18
|
+
* Resolvable.get() either retrieves the Resolvable's existing promise, or else invokes resolve() (which invokes the
|
|
19
|
+
* resolveFn) and returns the resulting promise.
|
|
20
|
+
*
|
|
21
|
+
* Resolvable.get() and Resolvable.resolve() both execute within a context path, which is passed as the first
|
|
22
|
+
* parameter to those fns.
|
|
23
|
+
*/
|
|
24
|
+
export class Resolvable {
|
|
25
|
+
constructor(arg1, resolveFn, deps, policy, data) {
|
|
26
|
+
this.resolved = false;
|
|
27
|
+
this.promise = undefined;
|
|
28
|
+
if (arg1 instanceof Resolvable) {
|
|
29
|
+
extend(this, arg1);
|
|
30
|
+
} else if (isFunction(resolveFn)) {
|
|
31
|
+
if (isNullOrUndefined(arg1))
|
|
32
|
+
throw new Error("new Resolvable(): token argument is required");
|
|
33
|
+
if (!isFunction(resolveFn))
|
|
34
|
+
throw new Error(
|
|
35
|
+
"new Resolvable(): resolveFn argument must be a function",
|
|
36
|
+
);
|
|
37
|
+
this.token = arg1;
|
|
38
|
+
this.policy = policy;
|
|
39
|
+
this.resolveFn = resolveFn;
|
|
40
|
+
this.deps = deps || [];
|
|
41
|
+
this.data = data;
|
|
42
|
+
this.resolved = data !== undefined;
|
|
43
|
+
this.promise = this.resolved ? services.$q.when(this.data) : undefined;
|
|
44
|
+
} else if (
|
|
45
|
+
isObject(arg1) &&
|
|
46
|
+
arg1.token &&
|
|
47
|
+
(arg1.hasOwnProperty("resolveFn") || arg1.hasOwnProperty("data"))
|
|
48
|
+
) {
|
|
49
|
+
const literal = arg1;
|
|
50
|
+
return new Resolvable(
|
|
51
|
+
literal.token,
|
|
52
|
+
literal.resolveFn,
|
|
53
|
+
literal.deps,
|
|
54
|
+
literal.policy,
|
|
55
|
+
literal.data,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
getPolicy(state) {
|
|
60
|
+
const thisPolicy = this.policy || {};
|
|
61
|
+
const statePolicy = (state && state.resolvePolicy) || {};
|
|
62
|
+
return {
|
|
63
|
+
when: thisPolicy.when || statePolicy.when || defaultResolvePolicy.when,
|
|
64
|
+
async:
|
|
65
|
+
thisPolicy.async || statePolicy.async || defaultResolvePolicy.async,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Asynchronously resolve this Resolvable's data
|
|
70
|
+
*
|
|
71
|
+
* Given a ResolveContext that this Resolvable is found in:
|
|
72
|
+
* Wait for this Resolvable's dependencies, then invoke this Resolvable's function
|
|
73
|
+
* and update the Resolvable's state
|
|
74
|
+
*/
|
|
75
|
+
resolve(resolveContext, trans) {
|
|
76
|
+
const $q = services.$q;
|
|
77
|
+
// Gets all dependencies from ResolveContext and wait for them to be resolved
|
|
78
|
+
const getResolvableDependencies = () =>
|
|
79
|
+
$q.all(
|
|
80
|
+
resolveContext
|
|
81
|
+
.getDependencies(this)
|
|
82
|
+
.map((resolvable) => resolvable.get(resolveContext, trans)),
|
|
83
|
+
);
|
|
84
|
+
// Invokes the resolve function passing the resolved dependencies as arguments
|
|
85
|
+
const invokeResolveFn = (resolvedDeps) =>
|
|
86
|
+
this.resolveFn.apply(null, resolvedDeps);
|
|
87
|
+
const node = resolveContext.findNode(this);
|
|
88
|
+
const state = node && node.state;
|
|
89
|
+
const asyncPolicy = this.getPolicy(state).async;
|
|
90
|
+
const customAsyncPolicy = isFunction(asyncPolicy) ? asyncPolicy : identity;
|
|
91
|
+
// After the final value has been resolved, update the state of the Resolvable
|
|
92
|
+
const applyResolvedValue = (resolvedValue) => {
|
|
93
|
+
this.data = resolvedValue;
|
|
94
|
+
this.resolved = true;
|
|
95
|
+
this.resolveFn = null;
|
|
96
|
+
trace.traceResolvableResolved(this, trans);
|
|
97
|
+
return this.data;
|
|
98
|
+
};
|
|
99
|
+
// Sets the promise property first, then getsResolvableDependencies in the context of the promise chain. Always waits one tick.
|
|
100
|
+
return (this.promise = $q
|
|
101
|
+
.when()
|
|
102
|
+
.then(getResolvableDependencies)
|
|
103
|
+
.then(invokeResolveFn)
|
|
104
|
+
.then(customAsyncPolicy)
|
|
105
|
+
.then(applyResolvedValue));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Gets a promise for this Resolvable's data.
|
|
109
|
+
*
|
|
110
|
+
* Fetches the data and returns a promise.
|
|
111
|
+
* Returns the existing promise if it has already been fetched once.
|
|
112
|
+
*/
|
|
113
|
+
get(resolveContext, trans) {
|
|
114
|
+
return this.promise || this.resolve(resolveContext, trans);
|
|
115
|
+
}
|
|
116
|
+
toString() {
|
|
117
|
+
return `Resolvable(token: ${stringify(this.token)}, requires: [${this.deps.map(stringify)}])`;
|
|
118
|
+
}
|
|
119
|
+
clone() {
|
|
120
|
+
return new Resolvable(this);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
Resolvable.fromData = (token, data) =>
|
|
124
|
+
new Resolvable(token, () => data, null, null, data);
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { find, tail, uniqR, unnestR, inArray } from "../common/common";
|
|
2
|
+
import { propEq, not } from "../common/hof";
|
|
3
|
+
import { trace } from "../common/trace";
|
|
4
|
+
import { services } from "../common/coreservices";
|
|
5
|
+
import { resolvePolicies } from "./interface";
|
|
6
|
+
import { Resolvable } from "./resolvable";
|
|
7
|
+
import { PathUtils } from "../path/pathUtils";
|
|
8
|
+
import { stringify } from "../common/strings";
|
|
9
|
+
import { isUndefined } from "../../../core/utils";
|
|
10
|
+
|
|
11
|
+
const whens = resolvePolicies.when;
|
|
12
|
+
const ALL_WHENS = [whens.EAGER, whens.LAZY];
|
|
13
|
+
const EAGER_WHENS = [whens.EAGER];
|
|
14
|
+
// tslint:disable-next-line:no-inferrable-types
|
|
15
|
+
export const NATIVE_INJECTOR_TOKEN = "Native Injector";
|
|
16
|
+
/**
|
|
17
|
+
* Encapsulates Dependency Injection for a path of nodes
|
|
18
|
+
*
|
|
19
|
+
* UI-Router states are organized as a tree.
|
|
20
|
+
* A nested state has a path of ancestors to the root of the tree.
|
|
21
|
+
* When a state is being activated, each element in the path is wrapped as a [[PathNode]].
|
|
22
|
+
* A `PathNode` is a stateful object that holds things like parameters and resolvables for the state being activated.
|
|
23
|
+
*
|
|
24
|
+
* The ResolveContext closes over the [[PathNode]]s, and provides DI for the last node in the path.
|
|
25
|
+
*/
|
|
26
|
+
export class ResolveContext {
|
|
27
|
+
constructor(_path) {
|
|
28
|
+
this._path = _path;
|
|
29
|
+
}
|
|
30
|
+
/** Gets all the tokens found in the resolve context, de-duplicated */
|
|
31
|
+
getTokens() {
|
|
32
|
+
return this._path
|
|
33
|
+
.reduce(
|
|
34
|
+
(acc, node) => acc.concat(node.resolvables.map((r) => r.token)),
|
|
35
|
+
[],
|
|
36
|
+
)
|
|
37
|
+
.reduce(uniqR, []);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Gets the Resolvable that matches the token
|
|
41
|
+
*
|
|
42
|
+
* Gets the last Resolvable that matches the token in this context, or undefined.
|
|
43
|
+
* Throws an error if it doesn't exist in the ResolveContext
|
|
44
|
+
*/
|
|
45
|
+
getResolvable(token) {
|
|
46
|
+
const matching = this._path
|
|
47
|
+
.map((node) => node.resolvables)
|
|
48
|
+
.reduce(unnestR, [])
|
|
49
|
+
.filter((r) => r.token === token);
|
|
50
|
+
return tail(matching);
|
|
51
|
+
}
|
|
52
|
+
/** Returns the [[ResolvePolicy]] for the given [[Resolvable]] */
|
|
53
|
+
getPolicy(resolvable) {
|
|
54
|
+
const node = this.findNode(resolvable);
|
|
55
|
+
return resolvable.getPolicy(node.state);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns a ResolveContext that includes a portion of this one
|
|
59
|
+
*
|
|
60
|
+
* Given a state, this method creates a new ResolveContext from this one.
|
|
61
|
+
* The new context starts at the first node (root) and stops at the node for the `state` parameter.
|
|
62
|
+
*
|
|
63
|
+
* #### Why
|
|
64
|
+
*
|
|
65
|
+
* When a transition is created, the nodes in the "To Path" are injected from a ResolveContext.
|
|
66
|
+
* A ResolveContext closes over a path of [[PathNode]]s and processes the resolvables.
|
|
67
|
+
* The "To State" can inject values from its own resolvables, as well as those from all its ancestor state's (node's).
|
|
68
|
+
* This method is used to create a narrower context when injecting ancestor nodes.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* `let ABCD = new ResolveContext([A, B, C, D]);`
|
|
72
|
+
*
|
|
73
|
+
* Given a path `[A, B, C, D]`, where `A`, `B`, `C` and `D` are nodes for states `a`, `b`, `c`, `d`:
|
|
74
|
+
* When injecting `D`, `D` should have access to all resolvables from `A`, `B`, `C`, `D`.
|
|
75
|
+
* However, `B` should only be able to access resolvables from `A`, `B`.
|
|
76
|
+
*
|
|
77
|
+
* When resolving for the `B` node, first take the full "To Path" Context `[A,B,C,D]` and limit to the subpath `[A,B]`.
|
|
78
|
+
* `let AB = ABCD.subcontext(a)`
|
|
79
|
+
*/
|
|
80
|
+
subContext(state) {
|
|
81
|
+
return new ResolveContext(
|
|
82
|
+
PathUtils.subPath(this._path, (node) => node.state === state),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Adds Resolvables to the node that matches the state
|
|
87
|
+
*
|
|
88
|
+
* This adds a [[Resolvable]] (generally one created on the fly; not declared on a [[StateDeclaration.resolve]] block).
|
|
89
|
+
* The resolvable is added to the node matching the `state` parameter.
|
|
90
|
+
*
|
|
91
|
+
* These new resolvables are not automatically fetched.
|
|
92
|
+
* The calling code should either fetch them, fetch something that depends on them,
|
|
93
|
+
* or rely on [[resolvePath]] being called when some state is being entered.
|
|
94
|
+
*
|
|
95
|
+
* Note: each resolvable's [[ResolvePolicy]] is merged with the state's policy, and the global default.
|
|
96
|
+
*
|
|
97
|
+
* @param newResolvables the new Resolvables
|
|
98
|
+
* @param state Used to find the node to put the resolvable on
|
|
99
|
+
*/
|
|
100
|
+
addResolvables(newResolvables, state) {
|
|
101
|
+
const node = find(this._path, propEq("state", state));
|
|
102
|
+
const keys = newResolvables.map((r) => r.token);
|
|
103
|
+
node.resolvables = node.resolvables
|
|
104
|
+
.filter((r) => keys.indexOf(r.token) === -1)
|
|
105
|
+
.concat(newResolvables);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Returns a promise for an array of resolved path Element promises
|
|
109
|
+
*
|
|
110
|
+
* @param when
|
|
111
|
+
* @param trans
|
|
112
|
+
* @returns {Promise<any>|any}
|
|
113
|
+
*/
|
|
114
|
+
resolvePath(when = "LAZY", trans) {
|
|
115
|
+
// This option determines which 'when' policy Resolvables we are about to fetch.
|
|
116
|
+
const whenOption = inArray(ALL_WHENS, when) ? when : "LAZY";
|
|
117
|
+
// If the caller specified EAGER, only the EAGER Resolvables are fetched.
|
|
118
|
+
// if the caller specified LAZY, both EAGER and LAZY Resolvables are fetched.`
|
|
119
|
+
const matchedWhens =
|
|
120
|
+
whenOption === resolvePolicies.when.EAGER ? EAGER_WHENS : ALL_WHENS;
|
|
121
|
+
// get the subpath to the state argument, if provided
|
|
122
|
+
trace.traceResolvePath(this._path, when, trans);
|
|
123
|
+
const matchesPolicy = (acceptedVals, whenOrAsync) => (resolvable) =>
|
|
124
|
+
inArray(acceptedVals, this.getPolicy(resolvable)[whenOrAsync]);
|
|
125
|
+
// Trigger all the (matching) Resolvables in the path
|
|
126
|
+
// Reduce all the "WAIT" Resolvables into an array
|
|
127
|
+
const promises = this._path.reduce((acc, node) => {
|
|
128
|
+
const nodeResolvables = node.resolvables.filter(
|
|
129
|
+
matchesPolicy(matchedWhens, "when"),
|
|
130
|
+
);
|
|
131
|
+
const nowait = nodeResolvables.filter(matchesPolicy(["NOWAIT"], "async"));
|
|
132
|
+
const wait = nodeResolvables.filter(
|
|
133
|
+
not(matchesPolicy(["NOWAIT"], "async")),
|
|
134
|
+
);
|
|
135
|
+
// For the matching Resolvables, start their async fetch process.
|
|
136
|
+
const subContext = this.subContext(node.state);
|
|
137
|
+
const getResult = (r) =>
|
|
138
|
+
r
|
|
139
|
+
.get(subContext, trans)
|
|
140
|
+
// Return a tuple that includes the Resolvable's token
|
|
141
|
+
.then((value) => ({ token: r.token, value: value }));
|
|
142
|
+
nowait.forEach(getResult);
|
|
143
|
+
return acc.concat(wait.map(getResult));
|
|
144
|
+
}, []);
|
|
145
|
+
// Wait for all the "WAIT" resolvables
|
|
146
|
+
return services.$q.all(promises);
|
|
147
|
+
}
|
|
148
|
+
injector() {
|
|
149
|
+
return this._injector || (this._injector = new UIInjectorImpl(this));
|
|
150
|
+
}
|
|
151
|
+
findNode(resolvable) {
|
|
152
|
+
return find(this._path, (node) => inArray(node.resolvables, resolvable));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Gets the async dependencies of a Resolvable
|
|
156
|
+
*
|
|
157
|
+
* Given a Resolvable, returns its dependencies as a Resolvable[]
|
|
158
|
+
*/
|
|
159
|
+
getDependencies(resolvable) {
|
|
160
|
+
const node = this.findNode(resolvable);
|
|
161
|
+
// Find which other resolvables are "visible" to the `resolvable` argument
|
|
162
|
+
// subpath stopping at resolvable's node, or the whole path (if the resolvable isn't in the path)
|
|
163
|
+
const subPath =
|
|
164
|
+
PathUtils.subPath(this._path, (x) => x === node) || this._path;
|
|
165
|
+
const availableResolvables = subPath
|
|
166
|
+
.reduce((acc, _node) => acc.concat(_node.resolvables), []) // all of subpath's resolvables
|
|
167
|
+
.filter((res) => res !== resolvable); // filter out the `resolvable` argument
|
|
168
|
+
const getDependency = (token) => {
|
|
169
|
+
const matching = availableResolvables.filter((r) => r.token === token);
|
|
170
|
+
if (matching.length) return tail(matching);
|
|
171
|
+
const fromInjector = this.injector().getNative(token);
|
|
172
|
+
if (isUndefined(fromInjector)) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
"Could not find Dependency Injection token: " + stringify(token),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return new Resolvable(token, () => fromInjector, [], fromInjector);
|
|
178
|
+
};
|
|
179
|
+
return resolvable.deps.map(getDependency);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** @internal */
|
|
183
|
+
class UIInjectorImpl {
|
|
184
|
+
constructor(context) {
|
|
185
|
+
this.context = context;
|
|
186
|
+
this.native = this.get(NATIVE_INJECTOR_TOKEN) || services.$injector;
|
|
187
|
+
}
|
|
188
|
+
get(token) {
|
|
189
|
+
const resolvable = this.context.getResolvable(token);
|
|
190
|
+
if (resolvable) {
|
|
191
|
+
if (this.context.getPolicy(resolvable).async === "NOWAIT") {
|
|
192
|
+
return resolvable.get(this.context);
|
|
193
|
+
}
|
|
194
|
+
if (!resolvable.resolved) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
"Resolvable async .get() not complete:" + stringify(resolvable.token),
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return resolvable.data;
|
|
200
|
+
}
|
|
201
|
+
return this.getNative(token);
|
|
202
|
+
}
|
|
203
|
+
getAsync(token) {
|
|
204
|
+
const resolvable = this.context.getResolvable(token);
|
|
205
|
+
if (resolvable) return resolvable.get(this.context);
|
|
206
|
+
return services.$q.when(this.native.get(token));
|
|
207
|
+
}
|
|
208
|
+
getNative(token) {
|
|
209
|
+
return this.native && this.native.get(token);
|
|
210
|
+
}
|
|
211
|
+
}
|