diff --git a/src/directives/stateDirectives.ts b/src/directives/stateDirectives.ts index 78e67d552..f6ae66c54 100644 --- a/src/directives/stateDirectives.ts +++ b/src/directives/stateDirectives.ts @@ -511,6 +511,17 @@ uiStateDirective = ['$uiRouter', '$timeout', * * ``` * + * Arrays are also supported as values in the `ngClass`-like interface. + * This allows multiple states to add `active` class. + * + * #### Example: + * Given the following template, with "admin.roles" being the current state, the class will be added too: + * ```html + *
+ * Roles + *
+ * ``` + * * When the current state is "admin.roles" the "active" class will be applied to both the `
` and `` elements. * It is important to note that the state names/globs passed to `ui-sref-active` override any state provided by a linked `ui-sref`. * @@ -545,11 +556,21 @@ uiSrefActiveDirective = ['$state', '$stateParams', '$interpolate', '$uiRouter', } uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope); if (isObject(uiSrefActive)) { - forEach(uiSrefActive, function (stateOrName: StateOrName, activeClass: string) { - if (isString(stateOrName)) { + forEach(uiSrefActive, function (stateOrName: StateOrName|Array, activeClass: string) { + // Helper function to abstract adding state. + const addStateForClass = function (stateOrName: string, activeClass: string) { const ref = parseStateRef(stateOrName); addState(ref.state, $scope.$eval(ref.paramExpr), activeClass); } + if (isString(stateOrName)) { + // If state is string, just add it. + addStateForClass(stateOrName as string, activeClass) + } else if (isArray(stateOrName)) { + // If state is an array, iterate over it and add each array item individually. + forEach(stateOrName, function (stateOrName: string) { + addStateForClass(stateOrName, activeClass) + }); + } }); } diff --git a/test/stateDirectivesSpec.js b/test/stateDirectivesSpec.js index 5c4bb3f17..885cded6b 100644 --- a/test/stateDirectivesSpec.js +++ b/test/stateDirectivesSpec.js @@ -1112,4 +1112,49 @@ describe('uiSrefActive', function() { expect(el.hasClass('active')).toBeTruthy(); })); }); + + describe('ng-{class,style} interface, and handle values as arrays', function() { + it('should match on abstract states that are included by the current state', inject(function($rootScope, $compile, $state, $q) { + el = $compile('
Roles
')($rootScope); + $state.transitionTo('admin.roles'); + $q.flush(); + timeoutFlush(); + var abstractParent = el[0]; + expect(abstractParent.className).toMatch(/active/); + var child = el[0].querySelector('a'); + expect(child.className).toMatch(/active/); + })); + + it('should match on state parameters', inject(function($compile, $rootScope, $state, $q) { + el = $compile('
')($rootScope); + $state.transitionTo('admin.roles', {page: 1}); + $q.flush(); + timeoutFlush(); + expect(el[0].className).toMatch(/active/); + })); + + it('should support multiple pairs', inject(function($compile, $rootScope, $state, $q) { + el = $compile('
')($rootScope); + $state.transitionTo('contacts.item.detail', {id: 1, foo: 'bar'}); + $q.flush(); + timeoutFlush(); + expect(el[0].className).toMatch(/contacts/); + expect(el[0].className).not.toMatch(/admin/); + $state.transitionTo('admin.roles', {page: 1}); + $q.flush(); + timeoutFlush(); + expect(el[0].className).toMatch(/admin/); + expect(el[0].className).not.toMatch(/contacts/); + })); + + it('should update the active classes when compiled', inject(function($compile, $rootScope, $document, $state, $q) { + $state.transitionTo('admin.roles'); + $q.flush(); + timeoutFlush(); + el = $compile('
')($rootScope); + $rootScope.$digest(); + timeoutFlush(); + expect(el.hasClass('active')).toBeTruthy(); + })); + }); });