@@ -9,6 +9,17 @@ import {StateDeclaration} from "./interface";
9
9
import { BuilderFunction } from "./stateBuilder" ;
10
10
import { StateOrName } from "./interface" ;
11
11
import { UrlRouterProvider } from "../url/urlRouter" ;
12
+ import { removeFrom } from "../common/common" ;
13
+
14
+ /**
15
+ * The signature for the callback function provided to [[StateRegistry.onStateRegistryEvent]].
16
+ *
17
+ * This callback receives two parameters:
18
+ *
19
+ * @param event a string; either "registered" or "deregistered"
20
+ * @param states the list of [[StateDeclaration]]s that were registered (or deregistered).
21
+ */
22
+ export type StateRegistryListener = ( event : "registered" | "deregistered" , states : StateDeclaration [ ] ) => void ;
12
23
13
24
export class StateRegistry {
14
25
private _root : State ;
@@ -18,10 +29,12 @@ export class StateRegistry {
18
29
private builder : StateBuilder ;
19
30
stateQueue : StateQueueManager ;
20
31
32
+ listeners : StateRegistryListener [ ] = [ ] ;
33
+
21
34
constructor ( urlMatcherFactory : UrlMatcherFactory , urlRouterProvider : UrlRouterProvider ) {
22
35
this . matcher = new StateMatcher ( this . states ) ;
23
36
this . builder = new StateBuilder ( this . matcher , urlMatcherFactory ) ;
24
- this . stateQueue = new StateQueueManager ( this . states , this . builder , urlRouterProvider ) ;
37
+ this . stateQueue = new StateQueueManager ( this . states , this . builder , urlRouterProvider , this . listeners ) ;
25
38
26
39
let rootStateDef : StateDeclaration = {
27
40
name : '' ,
@@ -37,16 +50,131 @@ export class StateRegistry {
37
50
_root . navigable = null ;
38
51
}
39
52
53
+ /**
54
+ * Listen for a State Registry events
55
+ *
56
+ * Adds a callback that is invoked when states are registered or deregistered with the StateRegistry.
57
+ *
58
+ * @example
59
+ * ```js
60
+ *
61
+ * let allStates = registry.get();
62
+ *
63
+ * // Later, invoke deregisterFn() to remove the listener
64
+ * let deregisterFn = registry.onStatesChanged((event, states) => {
65
+ * switch(event) {
66
+ * case: 'registered':
67
+ * states.forEach(state => allStates.push(state));
68
+ * break;
69
+ * case: 'deregistered':
70
+ * states.forEach(state => {
71
+ * let idx = allStates.indexOf(state);
72
+ * if (idx !== -1) allStates.splice(idx, 1);
73
+ * });
74
+ * break;
75
+ * }
76
+ * });
77
+ * ```
78
+ *
79
+ * @param listener a callback function invoked when the registered states changes.
80
+ * The function receives two parameters, `event` and `state`.
81
+ * See [[StateRegistryListener]]
82
+ * @return a function that deregisters the listener
83
+ */
84
+ onStatesChanged ( listener : StateRegistryListener ) : ( ) => void {
85
+ this . listeners . push ( listener ) ;
86
+ return function deregisterListener ( ) {
87
+ removeFrom ( this . listeners ) ( listener ) ;
88
+ } . bind ( this ) ;
89
+ }
90
+
91
+ /**
92
+ * Gets the implicit root state
93
+ *
94
+ * Gets the root of the state tree.
95
+ * The root state is implicitly created by UI-Router.
96
+ * Note: this returns the internal [[State]] representation, not a [[StateDeclaration]]
97
+ *
98
+ * @return the root [[State]]
99
+ */
40
100
root ( ) {
41
101
return this . _root ;
42
102
}
43
103
44
- register ( stateDefinition : StateDeclaration ) {
104
+ /**
105
+ * Adds a state to the registry
106
+ *
107
+ * Registers a [[StateDefinition]] or queues it for registration.
108
+ *
109
+ * Note: a state will be queued if the state's parent isn't yet registered.
110
+ * It will also be queued if the queue is not yet in [[StateQueueManager.autoFlush]] mode.
111
+ *
112
+ * @param stateDefinition the definition of the state to register.
113
+ * @returns the internal [[State]] object.
114
+ * If the state was successfully registered, then the object is fully built (See: [[StateBuilder]]).
115
+ * If the state was only queued, then the object is not fully built.
116
+ */
117
+ register ( stateDefinition : StateDeclaration ) : State {
45
118
return this . stateQueue . register ( stateDefinition ) ;
46
119
}
47
120
121
+ /** @hidden */
122
+ private _deregisterTree ( state : State ) {
123
+ let all = this . get ( ) . map ( s => s . $$state ( ) ) ;
124
+ const getChildren = ( states : State [ ] ) => {
125
+ let children = all . filter ( s => states . indexOf ( s . parent ) !== - 1 ) ;
126
+ return children . length === 0 ? children : children . concat ( getChildren ( children ) ) ;
127
+ } ;
128
+
129
+ let children = getChildren ( [ state ] ) ;
130
+ let deregistered = [ state ] . concat ( children ) . reverse ( ) ;
131
+
132
+ deregistered . forEach ( state => {
133
+ state . url && state . url . config . $$removeRule ( ) ;
134
+ delete this . states [ state . name ] ;
135
+ } ) ;
136
+
137
+ return deregistered ;
138
+ }
139
+
140
+ /**
141
+ * Removes a state from the registry
142
+ *
143
+ * This removes a state from the registry.
144
+ * If the state has children, they are are also removed from the registry.
145
+ *
146
+ * @param stateOrName the state's name or object representation
147
+ * @returns {State[] } a list of removed states
148
+ */
149
+ deregister ( stateOrName : StateOrName ) {
150
+ let _state = this . get ( stateOrName ) ;
151
+ if ( ! _state ) throw new Error ( "Can't deregister state; not found: " + stateOrName ) ;
152
+ let deregisteredStates = this . _deregisterTree ( _state . $$state ( ) ) ;
153
+
154
+ this . listeners . forEach ( listener => listener ( "deregistered" , deregisteredStates . map ( s => s . self ) ) ) ;
155
+ return deregisteredStates ;
156
+ }
157
+
158
+ /**
159
+ * Gets all registered states
160
+ *
161
+ * Calling this method with no arguments will return a list of all the states that are currently registered.
162
+ * Note: this does not return states that are *queued* but not yet registered.
163
+ *
164
+ * @return a list of [[StateDeclaration]]s
165
+ */
48
166
get ( ) : StateDeclaration [ ] ;
49
- get ( stateOrName : StateOrName , base : StateOrName ) : StateDeclaration ;
167
+
168
+ /**
169
+ * Gets a registered state
170
+ *
171
+ * Given a state or a name, finds and returns the [[StateDeclaration]] from the registry.
172
+ * Note: this does not return states that are *queued* but not yet registered.
173
+ *
174
+ * @param stateOrName either the name of a state, or a state object.
175
+ * @return a registered [[StateDeclaration]] that matched the `stateOrName`, or null if the state isn't registered.
176
+ */
177
+ get ( stateOrName : StateOrName , base ?: StateOrName ) : StateDeclaration ;
50
178
get ( stateOrName ?: StateOrName , base ?: StateOrName ) : any {
51
179
if ( arguments . length === 0 )
52
180
return < StateDeclaration [ ] > Object . keys ( this . states ) . map ( name => this . states [ name ] . self ) ;
0 commit comments