Skip to content

Commit

Permalink
feat(uiSrefActive): Support arrays of globs for ng-class style
Browse files Browse the repository at this point in the history
i.e., <div ui-sref-active="{ active: ['randomState.**', admin.roles'] }"></div>

Closes #2524
  • Loading branch information
christopherthielen committed Jan 30, 2018
2 parents 0a807ae + 4c8a6d6 commit b215343
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 6 deletions.
32 changes: 26 additions & 6 deletions src/directives/stateDirectives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,17 @@ uiStateDirective = ['$uiRouter', '$timeout',
* </div>
* ```
*
* 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
* <div ui-sref-active="{'active': ['owner.**', 'admin.**']}">
* <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
* </div>
* ```
*
* When the current state is "admin.roles" the "active" class will be applied to both the `<div>` and `<a>` 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`.
*
Expand Down Expand Up @@ -543,9 +554,7 @@ uiSrefActiveDirective = ['$state', '$stateParams', '$interpolate', '$uiRouter',
// Do nothing. uiSrefActive is not a valid expression.
// Fall back to using $interpolate below
}
if (!uiSrefActive) {
uiSrefActive = $interpolate($attrs.uiSrefActive || '', false)($scope);
}
uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
setStatesFromDefinitionObject(uiSrefActive);

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

function setStatesFromDefinitionObject (statesDefinition: any) {
function setStatesFromDefinitionObject (statesDefinition: object) {
if (isObject(statesDefinition)) {
states = [];
forEach(statesDefinition, function (stateOrName: StateOrName, activeClass: string) {
if (isString(stateOrName)) {
forEach(statesDefinition, function (stateOrName: StateOrName | Array<StateOrName>, 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)
});
}
});
}
Expand Down
45 changes: 45 additions & 0 deletions test/stateDirectivesSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1162,4 +1162,49 @@ describe('uiSrefActive', function() {
expect(template.eq(1).hasClass('active')).toBeFalsy();
}));
});

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('<div ui-sref-active="{active: [\'randomState.**\', \'admin.roles\']}"><a ui-sref-active="active" ui-sref="admin.roles">Roles</a></div>')($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('<div ui-sref-active="{active: [\'admin.roles({page: 1})\']}"></div>')($rootScope);
$state.transitionTo('admin.roles', {page: 1});
$q.flush();
timeoutFlush();
expect(el[0].className).toMatch(/active/);
}));

it('should support multiple <className, stateOrName> pairs', inject(function($compile, $rootScope, $state, $q) {
el = $compile('<div ui-sref-active="{contacts: [\'contacts.item\', \'contacts.item.detail\'], admin: \'admin.roles({page: 1})\'}"></div>')($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('<div ui-sref-active="{active: [\'admin.roles\', \'admin.someOtherState\']}"/>')($rootScope);
$rootScope.$digest();
timeoutFlush();
expect(el.hasClass('active')).toBeTruthy();
}));
});
});

0 comments on commit b215343

Please sign in to comment.