Skip to content

Commit b215343

Browse files
feat(uiSrefActive): Support arrays of globs for ng-class style
i.e., <div ui-sref-active="{ active: ['randomState.**', admin.roles'] }"></div> Closes #2524
2 parents 0a807ae + 4c8a6d6 commit b215343

File tree

2 files changed

+71
-6
lines changed

2 files changed

+71
-6
lines changed

src/directives/stateDirectives.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,17 @@ uiStateDirective = ['$uiRouter', '$timeout',
511511
* </div>
512512
* ```
513513
*
514+
* Arrays are also supported as values in the `ngClass`-like interface.
515+
* This allows multiple states to add `active` class.
516+
*
517+
* #### Example:
518+
* Given the following template, with "admin.roles" being the current state, the class will be added too:
519+
* ```html
520+
* <div ui-sref-active="{'active': ['owner.**', 'admin.**']}">
521+
* <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
522+
* </div>
523+
* ```
524+
*
514525
* When the current state is "admin.roles" the "active" class will be applied to both the `<div>` and `<a>` elements.
515526
* It is important to note that the state names/globs passed to `ui-sref-active` override any state provided by a linked `ui-sref`.
516527
*
@@ -543,9 +554,7 @@ uiSrefActiveDirective = ['$state', '$stateParams', '$interpolate', '$uiRouter',
543554
// Do nothing. uiSrefActive is not a valid expression.
544555
// Fall back to using $interpolate below
545556
}
546-
if (!uiSrefActive) {
547-
uiSrefActive = $interpolate($attrs.uiSrefActive || '', false)($scope);
548-
}
557+
uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
549558
setStatesFromDefinitionObject(uiSrefActive);
550559

551560
// Allow uiSref to communicate with uiSrefActive[Equals]
@@ -583,13 +592,24 @@ uiSrefActiveDirective = ['$state', '$stateParams', '$interpolate', '$uiRouter',
583592
setStatesFromDefinitionObject(uiSrefActive);
584593
}
585594

586-
function setStatesFromDefinitionObject (statesDefinition: any) {
595+
function setStatesFromDefinitionObject (statesDefinition: object) {
587596
if (isObject(statesDefinition)) {
588597
states = [];
589-
forEach(statesDefinition, function (stateOrName: StateOrName, activeClass: string) {
590-
if (isString(stateOrName)) {
598+
forEach(statesDefinition, function (stateOrName: StateOrName | Array<StateOrName>, activeClass: string) {
599+
// Helper function to abstract adding state.
600+
const addStateForClass = function (stateOrName: string, activeClass: string) {
591601
const ref = parseStateRef(stateOrName);
592602
addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
603+
};
604+
605+
if (isString(stateOrName)) {
606+
// If state is string, just add it.
607+
addStateForClass(stateOrName as string, activeClass)
608+
} else if (isArray(stateOrName)) {
609+
// If state is an array, iterate over it and add each array item individually.
610+
forEach(stateOrName, function (stateOrName: string) {
611+
addStateForClass(stateOrName, activeClass)
612+
});
593613
}
594614
});
595615
}

test/stateDirectivesSpec.js

+45
Original file line numberDiff line numberDiff line change
@@ -1162,4 +1162,49 @@ describe('uiSrefActive', function() {
11621162
expect(template.eq(1).hasClass('active')).toBeFalsy();
11631163
}));
11641164
});
1165+
1166+
describe('ng-{class,style} interface, and handle values as arrays', function() {
1167+
it('should match on abstract states that are included by the current state', inject(function($rootScope, $compile, $state, $q) {
1168+
el = $compile('<div ui-sref-active="{active: [\'randomState.**\', \'admin.roles\']}"><a ui-sref-active="active" ui-sref="admin.roles">Roles</a></div>')($rootScope);
1169+
$state.transitionTo('admin.roles');
1170+
$q.flush();
1171+
timeoutFlush();
1172+
var abstractParent = el[0];
1173+
expect(abstractParent.className).toMatch(/active/);
1174+
var child = el[0].querySelector('a');
1175+
expect(child.className).toMatch(/active/);
1176+
}));
1177+
1178+
it('should match on state parameters', inject(function($compile, $rootScope, $state, $q) {
1179+
el = $compile('<div ui-sref-active="{active: [\'admin.roles({page: 1})\']}"></div>')($rootScope);
1180+
$state.transitionTo('admin.roles', {page: 1});
1181+
$q.flush();
1182+
timeoutFlush();
1183+
expect(el[0].className).toMatch(/active/);
1184+
}));
1185+
1186+
it('should support multiple <className, stateOrName> pairs', inject(function($compile, $rootScope, $state, $q) {
1187+
el = $compile('<div ui-sref-active="{contacts: [\'contacts.item\', \'contacts.item.detail\'], admin: \'admin.roles({page: 1})\'}"></div>')($rootScope);
1188+
$state.transitionTo('contacts.item.detail', {id: 1, foo: 'bar'});
1189+
$q.flush();
1190+
timeoutFlush();
1191+
expect(el[0].className).toMatch(/contacts/);
1192+
expect(el[0].className).not.toMatch(/admin/);
1193+
$state.transitionTo('admin.roles', {page: 1});
1194+
$q.flush();
1195+
timeoutFlush();
1196+
expect(el[0].className).toMatch(/admin/);
1197+
expect(el[0].className).not.toMatch(/contacts/);
1198+
}));
1199+
1200+
it('should update the active classes when compiled', inject(function($compile, $rootScope, $document, $state, $q) {
1201+
$state.transitionTo('admin.roles');
1202+
$q.flush();
1203+
timeoutFlush();
1204+
el = $compile('<div ui-sref-active="{active: [\'admin.roles\', \'admin.someOtherState\']}"/>')($rootScope);
1205+
$rootScope.$digest();
1206+
timeoutFlush();
1207+
expect(el.hasClass('active')).toBeTruthy();
1208+
}));
1209+
});
11651210
});

0 commit comments

Comments
 (0)