Skip to content

Commit d9a676b

Browse files
committed
feat(uiSrefActive): allow active & active-eq on same element
closes #1997
1 parent dba25db commit d9a676b

File tree

2 files changed

+66
-23
lines changed

2 files changed

+66
-23
lines changed

src/stateDirectives.js

+22-21
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,14 @@ $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
228228
function $StateRefActiveDirective($state, $stateParams, $interpolate) {
229229
return {
230230
restrict: "A",
231-
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
232-
var states = [], activeClass;
231+
controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) {
232+
var states = [], activeClass, activeEqClass;
233233

234234
// There probably isn't much point in $observing this
235235
// uiSrefActive and uiSrefActiveEq share the same directive object with some
236236
// slight difference in logic routing
237-
activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
237+
activeClass = $interpolate($attrs.uiSrefActive || '', false)($scope);
238+
activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);
238239

239240
// Allow uiSref to communicate with uiSrefActive[Equals]
240241
this.$$addStateInfo = function (newState, newParams) {
@@ -252,29 +253,29 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) {
252253

253254
// Update route state
254255
function update() {
255-
if (anyMatch()) {
256-
$element.addClass(activeClass);
257-
} else {
258-
$element.removeClass(activeClass);
259-
}
260-
}
261-
262-
function anyMatch() {
263256
for (var i = 0; i < states.length; i++) {
264-
if (isMatch(states[i].state, states[i].params)) {
265-
return true;
257+
if (anyMatch(states[i].state, states[i].params)) {
258+
addClass($element, activeClass);
259+
} else {
260+
removeClass($element, activeClass);
266261
}
267-
}
268-
return false;
269-
}
270262

271-
function isMatch(state, params) {
272-
if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
273-
return $state.is(state.name, params);
274-
} else {
275-
return $state.includes(state.name, params);
263+
if (exactMatch(states[i].state, states[i].params)) {
264+
addClass($element, activeEqClass);
265+
} else {
266+
removeClass($element, activeEqClass);
267+
}
276268
}
277269
}
270+
271+
function addClass(el, className) { $timeout(function () { el.addClass(className); }); }
272+
273+
function removeClass(el, className) { el.removeClass(className); }
274+
275+
function anyMatch(state, params) { return $state.includes(state.name, params); }
276+
277+
function exactMatch(state, params) { return $state.is(state.name, params); }
278+
278279
}]
279280
};
280281
}

test/stateDirectivesSpec.js

+44-2
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,16 @@ describe('uiSrefActive', function() {
416416
});
417417
}));
418418

419-
beforeEach(inject(function($document) {
419+
beforeEach(inject(function($document, $timeout) {
420420
document = $document[0];
421+
timeoutFlush = function () {
422+
try {
423+
$timeout.flush();
424+
} catch (e) {
425+
// Angular 1.0.8 throws 'No deferred tasks to be flushed' if there is nothing in queue.
426+
// Behave as Angular >=1.1.5 and do nothing in such case.
427+
}
428+
}
421429
}));
422430

423431
it('should update class for sibling uiSref', inject(function($rootScope, $q, $compile, $state) {
@@ -428,11 +436,12 @@ describe('uiSrefActive', function() {
428436
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
429437
$state.transitionTo('contacts.item', { id: 1 });
430438
$q.flush();
431-
439+
timeoutFlush();
432440
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
433441

434442
$state.transitionTo('contacts.item', { id: 2 });
435443
$q.flush();
444+
timeoutFlush();
436445
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
437446
}));
438447

@@ -444,10 +453,12 @@ describe('uiSrefActive', function() {
444453
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
445454
$state.transitionTo('contacts.item.detail', { id: 5, foo: 'bar' });
446455
$q.flush();
456+
timeoutFlush();
447457
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
448458

449459
$state.transitionTo('contacts.item.detail', { id: 5, foo: 'baz' });
450460
$q.flush();
461+
timeoutFlush();
451462
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
452463
}));
453464

@@ -458,10 +469,12 @@ describe('uiSrefActive', function() {
458469

459470
$state.transitionTo('contacts.item.edit', { id: 1 });
460471
$q.flush();
472+
timeoutFlush();
461473
expect(a.attr('class')).toMatch(/active/);
462474

463475
$state.transitionTo('contacts.item.edit', { id: 4 });
464476
$q.flush();
477+
timeoutFlush();
465478
expect(a.attr('class')).not.toMatch(/active/);
466479
}));
467480

@@ -472,28 +485,51 @@ describe('uiSrefActive', function() {
472485

473486
$state.transitionTo('contacts.item', { id: 1 });
474487
$q.flush();
488+
timeoutFlush();
475489
expect(a.attr('class')).toMatch(/active/);
476490

477491
$state.transitionTo('contacts.item.edit', { id: 1 });
478492
$q.flush();
493+
timeoutFlush();
479494
expect(a.attr('class')).not.toMatch(/active/);
480495
}));
481496

497+
it('should match on child states when active-equals and active-equals-eq is used', inject(function($rootScope, $q, $compile, $state, $timeout) {
498+
template = $compile('<div><a ui-sref="contacts.item({ id: 1 })" ui-sref-active="active" ui-sref-active-eq="active-eq">Contacts</a></div>')($rootScope);
499+
$rootScope.$digest();
500+
var a = angular.element(template[0].getElementsByTagName('a')[0]);
501+
502+
$state.transitionTo('contacts.item', { id: 1 });
503+
$q.flush();
504+
timeoutFlush();
505+
expect(a.attr('class')).toMatch(/active/);
506+
expect(a.attr('class')).toMatch(/active-eq/);
507+
508+
$state.transitionTo('contacts.item.edit', { id: 1 });
509+
$q.flush();
510+
timeoutFlush();
511+
expect(a.attr('class')).toMatch(/active/);
512+
expect(a.attr('class')).not.toMatch(/active-eq/);
513+
}));
514+
482515
it('should resolve relative state refs', inject(function($rootScope, $q, $compile, $state) {
483516
el = angular.element('<section><div ui-view></div></section>');
484517
template = $compile(el)($rootScope);
485518
$rootScope.$digest();
486519

487520
$state.transitionTo('contacts');
488521
$q.flush();
522+
timeoutFlush();
489523
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope');
490524

491525
$state.transitionTo('contacts.item', { id: 6 });
492526
$q.flush();
527+
timeoutFlush();
493528
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope active');
494529

495530
$state.transitionTo('contacts.item', { id: 5 });
496531
$q.flush();
532+
timeoutFlush();
497533
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope');
498534
}));
499535

@@ -506,10 +542,12 @@ describe('uiSrefActive', function() {
506542

507543
$state.transitionTo('contacts.item', { id: 1 });
508544
$q.flush();
545+
timeoutFlush();
509546
expect(angular.element(template[0]).attr('class')).toBe('ng-scope active');
510547

511548
$state.transitionTo('contacts.item', { id: 2 });
512549
$q.flush();
550+
timeoutFlush();
513551
expect(angular.element(template[0]).attr('class')).toBe('ng-scope active');
514552
}));
515553

@@ -524,10 +562,12 @@ describe('uiSrefActive', function() {
524562

525563
$state.transitionTo('contacts.item', { id: 1 });
526564
$q.flush();
565+
timeoutFlush();
527566
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
528567

529568
$state.transitionTo('contacts.lazy');
530569
$q.flush();
570+
timeoutFlush();
531571
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
532572
}));
533573

@@ -542,10 +582,12 @@ describe('uiSrefActive', function() {
542582

543583
$state.transitionTo('contacts.item', { id: 1 });
544584
$q.flush();
585+
timeoutFlush();
545586
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
546587

547588
$state.transitionTo('contacts.lazy');
548589
$q.flush();
590+
timeoutFlush();
549591
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
550592
}));
551593
});

0 commit comments

Comments
 (0)