diff --git a/src/stateDirectives.js b/src/stateDirectives.js index 09991030c..8f23b1fdb 100644 --- a/src/stateDirectives.js +++ b/src/stateDirectives.js @@ -228,13 +228,14 @@ $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate']; function $StateRefActiveDirective($state, $stateParams, $interpolate) { return { restrict: "A", - controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) { - var states = [], activeClass; + controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) { + var states = [], activeClass, activeEqClass; // There probably isn't much point in $observing this // uiSrefActive and uiSrefActiveEq share the same directive object with some // slight difference in logic routing - activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope); + activeClass = $interpolate($attrs.uiSrefActive || '', false)($scope); + activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope); // Allow uiSref to communicate with uiSrefActive[Equals] this.$$addStateInfo = function (newState, newParams) { @@ -252,29 +253,29 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) { // Update route state function update() { - if (anyMatch()) { - $element.addClass(activeClass); - } else { - $element.removeClass(activeClass); - } - } - - function anyMatch() { for (var i = 0; i < states.length; i++) { - if (isMatch(states[i].state, states[i].params)) { - return true; + if (anyMatch(states[i].state, states[i].params)) { + addClass($element, activeClass); + } else { + removeClass($element, activeClass); } - } - return false; - } - function isMatch(state, params) { - if (typeof $attrs.uiSrefActiveEq !== 'undefined') { - return $state.is(state.name, params); - } else { - return $state.includes(state.name, params); + if (exactMatch(states[i].state, states[i].params)) { + addClass($element, activeEqClass); + } else { + removeClass($element, activeEqClass); + } } } + + function addClass(el, className) { $timeout(function() { el.addClass(className); }); } + + function removeClass(el, className) { el.removeClass(className); } + + function anyMatch(state, params) { return $state.includes(state.name, params); } + + function exactMatch(state, params) { return $state.is(state.name, params); } + }] }; } diff --git a/test/stateDirectivesSpec.js b/test/stateDirectivesSpec.js index c9b621b7d..4e8a5c422 100644 --- a/test/stateDirectivesSpec.js +++ b/test/stateDirectivesSpec.js @@ -420,84 +420,113 @@ describe('uiSrefActive', function() { document = $document[0]; })); - it('should update class for sibling uiSref', inject(function($rootScope, $q, $compile, $state) { + it('should update class for sibling uiSref', inject(function($rootScope, $q, $compile, $state, $timeout) { el = angular.element('
'); template = $compile(el)($rootScope); $rootScope.$digest(); expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); $state.transitionTo('contacts.item', { id: 1 }); + $timeout.flush(); $q.flush(); expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active'); $state.transitionTo('contacts.item', { id: 2 }); + $timeout.flush(); $q.flush(); expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); })); - it('should match state\'s parameters', inject(function($rootScope, $q, $compile, $state) { + it('should match state\'s parameters', inject(function($rootScope, $q, $compile, $state, $timeout) { el = angular.element(''); template = $compile(el)($rootScope); $rootScope.$digest(); expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); $state.transitionTo('contacts.item.detail', { id: 5, foo: 'bar' }); + $timeout.flush(); $q.flush(); expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active'); $state.transitionTo('contacts.item.detail', { id: 5, foo: 'baz' }); + $timeout.flush(); $q.flush(); expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); })); - it('should match on child states', inject(function($rootScope, $q, $compile, $state) { + it('should match on child states', inject(function($rootScope, $q, $compile, $state, $timeout) { template = $compile('')($rootScope); $rootScope.$digest(); var a = angular.element(template[0].getElementsByTagName('a')[0]); $state.transitionTo('contacts.item.edit', { id: 1 }); + $timeout.flush(); $q.flush(); expect(a.attr('class')).toMatch(/active/); $state.transitionTo('contacts.item.edit', { id: 4 }); + $timeout.flush(); $q.flush(); expect(a.attr('class')).not.toMatch(/active/); })); - it('should NOT match on child states when active-equals is used', inject(function($rootScope, $q, $compile, $state) { + it('should NOT match on child states when active-equals is used', inject(function($rootScope, $q, $compile, $state, $timeout) { template = $compile('')($rootScope); $rootScope.$digest(); var a = angular.element(template[0].getElementsByTagName('a')[0]); $state.transitionTo('contacts.item', { id: 1 }); + $timeout.flush(); $q.flush(); expect(a.attr('class')).toMatch(/active/); $state.transitionTo('contacts.item.edit', { id: 1 }); + $timeout.flush(); $q.flush(); expect(a.attr('class')).not.toMatch(/active/); })); - it('should resolve relative state refs', inject(function($rootScope, $q, $compile, $state) { + it('should match on child states when active-equals and active-equals-eq is used', inject(function($rootScope, $q, $compile, $state, $timeout) { + template = $compile('')($rootScope); + $rootScope.$digest(); + var a = angular.element(template[0].getElementsByTagName('a')[0]); + + $state.transitionTo('contacts.item', { id: 1 }); + $timeout.flush(); + $q.flush(); + expect(a.attr('class')).toMatch(/active/); + expect(a.attr('class')).toMatch(/active-eq/); + + $state.transitionTo('contacts.item.edit', { id: 1 }); + $timeout.flush(); + $q.flush(); + expect(a.attr('class')).toMatch(/active/); + expect(a.attr('class')).not.toMatch(/active-eq/); + })); + + it('should resolve relative state refs', inject(function($rootScope, $q, $compile, $state, $timeout) { el = angular.element('