@@ -9,7 +9,7 @@ import {anyTrueR, tail, unnestR, Predicate} from "../../common/common";
9
9
import { Globals , UIRouterGlobals } from "../../globals" ;
10
10
import { Param } from "../../params/param" ;
11
11
import { PathFactory } from "../../path/pathFactory" ;
12
- import { Subscription , Observable } from "rxjs/Rx" ;
12
+ import { Subscription , Observable , BehaviorSubject } from "rxjs/Rx" ;
13
13
14
14
interface TransEvt { evt : string , trans : Transition }
15
15
@@ -106,14 +106,54 @@ function getSrefStatus(event: TransEvt, srefTarget: TargetState): SrefStatus {
106
106
} as SrefStatus ;
107
107
}
108
108
109
+ function mergeSrefStatus ( left : SrefStatus , right : SrefStatus ) {
110
+ return {
111
+ active : left . active || right . active ,
112
+ exact : left . exact || right . exact ,
113
+ entering : left . entering || right . entering ,
114
+ exiting : left . exiting || right . exiting ,
115
+ } ;
116
+ }
117
+
109
118
/**
110
- * A directive (which pairs with a [[UISref]]) and emits events when the UISref status changes.
119
+ * A directive which emits events when a paired [[UISref]] status changes.
120
+ *
121
+ * This directive is primarily used by the [[UISrefActive]]/[[UISrefActiveEq]] directives to monitor `UISref`(s).
122
+ * This directive shares the same attribute selectors as `UISrefActive/Eq`, so it is created whenever a `UISrefActive/Eq` is created.
123
+ *
124
+ * Most apps should simply use [[UISrefActive]], but some advanced components may want to process the
125
+ * `uiSrefStatus` events directly.
126
+ *
127
+ * ```js
128
+ * <li (uiSrefStatus)="onSrefStatusChanged($event)">
129
+ * <a uiSref="book" [uiParams]="{ bookId: book.id }">Book {{ book.name }}</a>
130
+ * </li>
131
+ * ```
132
+ *
133
+ * The `uiSrefStatus` event is emitted whenever an enclosed `uiSref`'s status changes.
134
+ * The event emitted is of type [[SrefStatus]], and has boolean values for `active`, `exact`, `entering`, and `exiting`.
111
135
*
112
- * This directive is used by the [[UISrefActive]] directive.
113
- *
114
- * The event emitted is of type [[SrefStatus]], and has boolean values for `active`, `exact`, `entering`, and `exiting`
115
- *
116
- * The values from this event can be captured and stored on a component, then applied (perhaps using ngClass).
136
+ * The values from this event can be captured and stored on a component (then applied, e.g., using ngClass).
137
+ *
138
+ * ---
139
+ *
140
+ * A single `uiSrefStatus` can enclose multiple `uiSref`.
141
+ * Each status boolean (`active`, `exact`, `entering`, `exiting`) will be true if *any of the enclosed `uiSref` status is true*.
142
+ * In other words, all enclosed `uiSref` statuses are merged to a single status using `||` (logical or).
143
+ *
144
+ * ```js
145
+ * <li (uiSrefStatus)="onSrefStatus($event)" uiSref="admin">
146
+ * Home
147
+ * <ul>
148
+ * <li> <a uiSref="admin.users">Users</a> </li>
149
+ * <li> <a uiSref="admin.groups">Groups</a> </li>
150
+ * </ul>
151
+ * </li>
152
+ * ```
153
+ *
154
+ * In the above example, `$event.active === true` when either `admin.users` or `admin.groups` is active.
155
+ *
156
+ * ---
117
157
*
118
158
* This API is subject to change.
119
159
*/
@@ -128,6 +168,8 @@ export class UISrefStatus {
128
168
status : SrefStatus ;
129
169
130
170
private _subscription : Subscription ;
171
+ private _srefChangesSub : Subscription ;
172
+ private _srefs$ : BehaviorSubject < UISref [ ] > ;
131
173
132
174
constructor ( @Inject ( Globals ) private _globals : UIRouterGlobals ) {
133
175
this . status = Object . assign ( { } , inactiveStatus ) ;
@@ -146,30 +188,31 @@ export class UISrefStatus {
146
188
return transStart$ . concat ( transFinish$ ) ;
147
189
} ) ;
148
190
149
- // Watch the children UISref components and get their target states
150
- let srefs$ : Observable < UISref [ ] > = Observable . of ( this . srefs . toArray ( ) ) . concat ( this . srefs . changes ) ;
191
+ // Watch the @ContentChildren UISref[] components and get their target states
192
+
193
+ // let srefs$: Observable<UISref[]> = Observable.of(this.srefs.toArray()).concat(this.srefs.changes);
194
+ this . _srefs$ = new BehaviorSubject ( this . srefs . toArray ( ) ) ;
195
+ this . _srefChangesSub = this . srefs . changes . subscribe ( srefs => this . _srefs$ . next ( srefs ) ) ;
196
+
151
197
let targetStates$ : Observable < TargetState [ ] > =
152
- srefs $. switchMap ( ( srefs : UISref [ ] ) =>
198
+ this . _srefs $. switchMap ( ( srefs : UISref [ ] ) =>
153
199
Observable . combineLatest < TargetState [ ] > ( srefs . map ( sref => sref . targetState$ ) ) ) ;
154
200
155
201
// Calculate the status of each UISref based on the transition event.
156
202
// Reduce the statuses (if multiple) by or-ing each flag.
157
203
this . _subscription = transEvents$ . mergeMap ( ( evt : TransEvt ) => {
158
204
return targetStates$ . map ( ( targets : TargetState [ ] ) => {
159
205
let statuses : SrefStatus [ ] = targets . map ( target => getSrefStatus ( evt , target ) ) ;
160
-
161
- return statuses . reduce ( ( acc : SrefStatus , val : SrefStatus ) => ( {
162
- active : acc . active || val . active ,
163
- exact : acc . active || val . active ,
164
- entering : acc . active || val . active ,
165
- exiting : acc . active || val . active ,
166
- } ) )
206
+ return statuses . reduce ( mergeSrefStatus )
167
207
} )
168
208
} ) . subscribe ( this . _setStatus . bind ( this ) ) ;
169
209
}
170
210
171
211
ngOnDestroy ( ) {
172
212
if ( this . _subscription ) this . _subscription . unsubscribe ( ) ;
213
+ if ( this . _srefChangesSub ) this . _srefChangesSub . unsubscribe ( ) ;
214
+ if ( this . _srefs$ ) this . _srefs$ . unsubscribe ( ) ;
215
+ this . _subscription = this . _srefChangesSub = this . _srefs$ = undefined ;
173
216
}
174
217
175
218
private _setStatus ( status : SrefStatus ) {
0 commit comments