@@ -9,6 +9,17 @@ import {StateDeclaration} from "./interface";
99import { BuilderFunction } from "./stateBuilder" ;
1010import { StateOrName } from "./interface" ;
1111import { 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 ;
1223
1324export class StateRegistry {
1425 private _root : State ;
@@ -18,10 +29,12 @@ export class StateRegistry {
1829 private builder : StateBuilder ;
1930 stateQueue : StateQueueManager ;
2031
32+ listeners : StateRegistryListener [ ] = [ ] ;
33+
2134 constructor ( urlMatcherFactory : UrlMatcherFactory , urlRouterProvider : UrlRouterProvider ) {
2235 this . matcher = new StateMatcher ( this . states ) ;
2336 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 ) ;
2538
2639 let rootStateDef : StateDeclaration = {
2740 name : '' ,
@@ -37,16 +50,131 @@ export class StateRegistry {
3750 _root . navigable = null ;
3851 }
3952
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+ */
40100 root ( ) {
41101 return this . _root ;
42102 }
43103
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 {
45118 return this . stateQueue . register ( stateDefinition ) ;
46119 }
47120
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+ */
48166 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 ;
50178 get ( stateOrName ?: StateOrName , base ?: StateOrName ) : any {
51179 if ( arguments . length === 0 )
52180 return < StateDeclaration [ ] > Object . keys ( this . states ) . map ( name => this . states [ name ] . self ) ;
0 commit comments