@@ -249,6 +249,24 @@ function $StateRefDynamicDirective($state, $timeout) {
249249 * </li>
250250 * </ul>
251251 * </pre>
252+ *
253+ * It is also possible to pass ui-sref-active an expression that evaluates
254+ * to an object hash, whose keys represent active class names and whose
255+ * values represent the respective state names/globs.
256+ * ui-sref-active will match if the current active state **includes** any of
257+ * the specified state names/globs, even the abstract ones.
258+ *
259+ * @Example
260+ * Given the following template, with "admin" being an abstract state:
261+ * <pre>
262+ * <div ui-sref-active="{'active': 'admin.*'}">
263+ * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
264+ * </div>
265+ * </pre>
266+ *
267+ * When the current state is "admin.roles" the "active" class will be applied
268+ * to both the <div> and <a> elements. It is important to note that the state
269+ * names/globs passed to ui-sref-active shadow the state provided by ui-sref.
252270 */
253271
254272/**
@@ -271,35 +289,75 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) {
271289 return {
272290 restrict : "A" ,
273291 controller : [ '$scope' , '$element' , '$attrs' , '$timeout' , function ( $scope , $element , $attrs , $timeout ) {
274- var states = [ ] , activeClass , activeEqClass ;
292+ var states = [ ] , activeClasses = { } , activeEqClass ;
275293
276294 // There probably isn't much point in $observing this
277295 // uiSrefActive and uiSrefActiveEq share the same directive object with some
278296 // slight difference in logic routing
279- activeClass = $interpolate ( $attrs . uiSrefActive || '' , false ) ( $scope ) ;
280297 activeEqClass = $interpolate ( $attrs . uiSrefActiveEq || '' , false ) ( $scope ) ;
281298
299+ var uiSrefActive = $scope . $eval ( $attrs . uiSrefActive ) || $interpolate ( $attrs . uiSrefActive || '' , false ) ( $scope ) ;
300+ if ( isObject ( uiSrefActive ) ) {
301+ forEach ( uiSrefActive , function ( stateOrName , activeClass ) {
302+ if ( isString ( stateOrName ) ) {
303+ var ref = parseStateRef ( stateOrName , $state . current . name ) ;
304+ addState ( ref . state , $scope . $eval ( ref . paramExpr ) , activeClass ) ;
305+ }
306+ } ) ;
307+ }
308+
282309 // Allow uiSref to communicate with uiSrefActive[Equals]
283310 this . $$addStateInfo = function ( newState , newParams ) {
284- var state = $state . get ( newState , stateContext ( $element ) ) ;
311+ // we already got an explicit state provided by ui-sref-active, so we
312+ // shadow the one that comes from ui-sref
313+ if ( isObject ( uiSrefActive ) && states . length > 0 ) {
314+ return ;
315+ }
316+ addState ( newState , newParams , uiSrefActive ) ;
317+ update ( ) ;
318+ } ;
319+
320+ $scope . $on ( '$stateChangeSuccess' , update ) ;
321+
322+ function addState ( stateName , stateParams , activeClass ) {
323+ var state = $state . get ( stateName , stateContext ( $element ) ) ;
324+ var stateHash = createStateHash ( stateName , stateParams ) ;
285325
286326 states . push ( {
287- state : state || { name : newState } ,
288- params : newParams
327+ state : state || { name : stateName } ,
328+ params : stateParams ,
329+ hash : stateHash
289330 } ) ;
290331
291- update ( ) ;
292- } ;
332+ activeClasses [ stateHash ] = activeClass ;
333+ }
293334
294- $scope . $on ( '$stateChangeSuccess' , update ) ;
335+ /**
336+ * @param {string } state
337+ * @param {Object|string } [params]
338+ * @return {string }
339+ */
340+ function createStateHash ( state , params ) {
341+ if ( ! isString ( state ) ) {
342+ throw new Error ( 'state should be a string' ) ;
343+ }
344+ if ( isObject ( params ) ) {
345+ return state + toJson ( params ) ;
346+ }
347+ params = $scope . $eval ( params ) ;
348+ if ( isObject ( params ) ) {
349+ return state + toJson ( params ) ;
350+ }
351+ return state ;
352+ }
295353
296354 // Update route state
297355 function update ( ) {
298356 for ( var i = 0 ; i < states . length ; i ++ ) {
299357 if ( anyMatch ( states [ i ] . state , states [ i ] . params ) ) {
300- addClass ( $element , activeClass ) ;
358+ addClass ( $element , activeClasses [ states [ i ] . hash ] ) ;
301359 } else {
302- removeClass ( $element , activeClass ) ;
360+ removeClass ( $element , activeClasses [ states [ i ] . hash ] ) ;
303361 }
304362
305363 if ( exactMatch ( states [ i ] . state , states [ i ] . params ) ) {
0 commit comments