diff --git a/src/stateDirectives.js b/src/stateDirectives.js index b39f7b6c8..eb7accb5d 100644 --- a/src/stateDirectives.js +++ b/src/stateDirectives.js @@ -173,12 +173,17 @@ function $StateRefDynamicDirective($state, $timeout) { var group = [attrs.uiState, attrs.uiStateParams || null, attrs.uiStateOpts || null]; var watch = '[' + group.map(function(val) { return val || 'null'; }).join(', ') + ']'; var def = { state: null, params: null, options: null, href: null }; + var unlinkInfoFn = null; function runStateRefLink (group) { def.state = group[0]; def.params = group[1]; def.options = group[2]; def.href = $state.href(def.state, def.params, def.options); - if (active) active.$$addStateInfo(def.state, def.params); + if (unlinkInfoFn) { + unlinkInfoFn(); + unlinkInfoFn = null; + } + if (active) unlinkInfoFn = active.$$addStateInfo(def.state, def.params); if (def.href) attrs.$set(type.attr, def.href); } @@ -319,8 +324,9 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) { if (isObject(uiSrefActive) && states.length > 0) { return; } - addState(newState, newParams, uiSrefActive); + var deregister = addState(newState, newParams, uiSrefActive); update(); + return deregister; }; $scope.$on('$stateChangeSuccess', update); @@ -329,13 +335,19 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) { var state = $state.get(stateName, stateContext($element)); var stateHash = createStateHash(stateName, stateParams); - states.push({ + var stateInfo = { state: state || { name: stateName }, params: stateParams, hash: stateHash - }); + }; + states.push(stateInfo); activeClasses[stateHash] = activeClass; + + return function removeState() { + var idx = states.indexOf(stateInfo); + if (idx !== -1) states.splice(idx, 1); + }; } /** diff --git a/test/stateDirectivesSpec.js b/test/stateDirectivesSpec.js index a9c0f120f..977b878f1 100644 --- a/test/stateDirectivesSpec.js +++ b/test/stateDirectivesSpec.js @@ -292,10 +292,11 @@ describe('uiStateRef', function() { }); describe('links with dynamic state definitions', function () { - var template; + var template, $state; - beforeEach(inject(function($rootScope, $compile, $state) { - el = angular.element('state'); + beforeEach(inject(function($rootScope, $compile, _$state_) { + $state = _$state_; + el = angular.element('state'); scope = $rootScope; angular.extend(scope, { state: 'contacts', params: {} }); template = $compile(el)(scope); @@ -326,6 +327,38 @@ describe('uiStateRef', function() { expect(angular.element(template[0]).attr('href')).toBe('#/contacts'); }); + it('updates a linked ui-sref-active', inject(function ($timeout) { + function tick() { scope.$digest(); try { $timeout.flush(); } catch (error) { } } + expect(template[0].className).not.toContain('active'); + expect(template[0].className).not.toContain('activeeq'); + + $state.go('contacts'); + tick(); + expect(template[0].className).toContain('active activeeq'); + + scope.state = 'contacts.item'; + scope.params = { id: 5 }; + tick(); + expect(template[0].className).not.toContain('active'); + expect(template[0].className).not.toContain('activeeq'); + + $state.go('contacts.item', { id: -5 }); + tick(); + expect(template[0].className).not.toContain('active'); + expect(template[0].className).not.toContain('activeeq'); + + $state.go('contacts.item', { id: 5 }); + tick(); + expect(template[0].className).toContain('active activeeq'); + + scope.state = 'contacts'; + scope.params = { }; + tick(); + expect(template[0].className).toContain('active'); + expect(template[0].className).not.toContain('activeeq'); + + })); + it('accepts param overrides', inject(function ($compile) { el = angular.element('state'); scope.state = 'contacts.item';