1
1
/// <reference path='../../typings/angularjs/angular.d.ts' />
2
2
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" ;
4
4
import trace from "../common/trace" ;
5
5
import { IPromise } from "angular" ;
6
6
import { IState } from "../state/interface" ;
7
7
import Path from "./path" ;
8
8
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
+ } ;
16
21
var defaultResolvePolicy = "jit" ; // TODO: make this configurable
17
22
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
+
18
46
/**
19
47
* An element in the path which represents a state and that state's Resolvables and their resolve statuses.
20
48
* When the resolved data is ready, it is stored in each Resolvable object within the PathElement
@@ -26,9 +54,8 @@ export default class PathElement {
26
54
constructor ( state : IState ) {
27
55
this . state = state ;
28
56
// 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 ) ;
32
59
}
33
60
34
61
state : IState ;
@@ -42,31 +69,28 @@ export default class PathElement {
42
69
return extend ( this . _resolvables , resolvablesByName ) ;
43
70
}
44
71
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.
47
76
resolvePathElement ( pathContext , options ) : IPromise < any > {
48
77
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 ) ;
63
88
64
- const matchesPolicy = ( resolvable ) => policyConf [ resolvable . name ] >= policyOrdinal ;
65
89
const getResolvePromise = ( resolvable ) => resolvable . get ( pathContext , options ) ;
90
+ let resolvablePromises : IPromises = < any > map ( matchingResolves , getResolvePromise ) ;
66
91
67
- var matchingResolves = filter ( resolvables , matchesPolicy ) ;
68
92
if ( options . trace ) trace . traceResolvePathElement ( this , matchingResolves , options ) ;
69
- var resolvablePromises = map ( matchingResolves , getResolvePromise ) ;
93
+
70
94
return runtime . $q . all ( resolvablePromises ) . then ( noop ) ;
71
95
}
72
96
@@ -79,11 +103,10 @@ export default class PathElement {
79
103
// pathContext is a Path which is used to retrieve dependent Resolvables for injecting
80
104
invokeLater ( fn , locals , pathContext , options ) : IPromise < any > {
81
105
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
+
87
110
return runtime . $q . all ( promises ) . then ( ( ) => {
88
111
try {
89
112
return this . invokeNow ( fn , locals , pathContext , options ) ;
@@ -99,15 +122,20 @@ export default class PathElement {
99
122
// Injects a function at this PathElement level with available Resolvables
100
123
// Does not wait until all Resolvables have been resolved; you must call PathElement.resolve() (or manually resolve each dep) first
101
124
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 ) ;
108
128
return runtime . $injector . invoke ( fn , this . state , combinedLocals ) ;
109
129
}
110
130
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
+
111
139
toString ( ) : string {
112
140
var state = parse ( "state.name" ) ( this ) || "(root)" ;
113
141
return `PathElement(${ state } )` ;
0 commit comments