mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-05 22:35:14 +08:00
fix(ngClass): handle ngClassOdd/Even affecting the same classes
The basic approach is to introduce a new elt.data() called $classCounts that keeps track of how many times ngClass, ngClassEven, or ngClassOdd tries to add a given class. The class is added only when the count goes from 0 to 1, and removed only when the count hits 0. To avoid duplicating work, some of the logic for checking which classes to add/remove move into this directive and the directive calls $animate. Closes #5271
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
function classDirective(name, selector) {
|
||||
name = 'ngClass' + name;
|
||||
return function() {
|
||||
return ['$animate', function($animate) {
|
||||
return {
|
||||
restrict: 'AC',
|
||||
link: function(scope, element, attr) {
|
||||
@@ -20,46 +20,100 @@ function classDirective(name, selector) {
|
||||
// jshint bitwise: false
|
||||
var mod = $index & 1;
|
||||
if (mod !== old$index & 1) {
|
||||
var classes = flattenClasses(scope.$eval(attr[name]));
|
||||
var classes = arrayClasses(scope.$eval(attr[name]));
|
||||
mod === selector ?
|
||||
attr.$addClass(classes) :
|
||||
attr.$removeClass(classes);
|
||||
addClasses(classes) :
|
||||
removeClasses(classes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addClasses(classes) {
|
||||
var newClasses = digestClassCounts(classes, 1);
|
||||
attr.$addClass(newClasses);
|
||||
}
|
||||
|
||||
function removeClasses(classes) {
|
||||
var newClasses = digestClassCounts(classes, -1);
|
||||
attr.$removeClass(newClasses);
|
||||
}
|
||||
|
||||
function digestClassCounts (classes, count) {
|
||||
var classCounts = element.data('$classCounts') || {};
|
||||
var classesToUpdate = [];
|
||||
forEach(classes, function (className) {
|
||||
if (count > 0 || classCounts[className]) {
|
||||
classCounts[className] = (classCounts[className] || 0) + count;
|
||||
if (classCounts[className] === +(count > 0)) {
|
||||
classesToUpdate.push(className);
|
||||
}
|
||||
}
|
||||
});
|
||||
element.data('$classCounts', classCounts);
|
||||
return classesToUpdate.join(' ');
|
||||
}
|
||||
|
||||
function updateClasses (oldClasses, newClasses) {
|
||||
var toAdd = arrayDifference(newClasses, oldClasses);
|
||||
var toRemove = arrayDifference(oldClasses, newClasses);
|
||||
toRemove = digestClassCounts(toRemove, -1);
|
||||
toAdd = digestClassCounts(toAdd, 1);
|
||||
|
||||
if (toAdd.length === 0) {
|
||||
$animate.removeClass(element, toRemove);
|
||||
} else if (toRemove.length === 0) {
|
||||
$animate.addClass(element, toAdd);
|
||||
} else {
|
||||
$animate.setClass(element, toAdd, toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
function ngClassWatchAction(newVal) {
|
||||
if (selector === true || scope.$index % 2 === selector) {
|
||||
var newClasses = flattenClasses(newVal || '');
|
||||
if(!oldVal) {
|
||||
attr.$addClass(newClasses);
|
||||
} else if(!equals(newVal,oldVal)) {
|
||||
attr.$updateClass(newClasses, flattenClasses(oldVal));
|
||||
var newClasses = arrayClasses(newVal || []);
|
||||
if (!oldVal) {
|
||||
addClasses(newClasses);
|
||||
} else if (!equals(newVal,oldVal)) {
|
||||
var oldClasses = arrayClasses(oldVal);
|
||||
updateClasses(oldClasses, newClasses);
|
||||
}
|
||||
}
|
||||
oldVal = copy(newVal);
|
||||
}
|
||||
|
||||
|
||||
function flattenClasses(classVal) {
|
||||
if(isArray(classVal)) {
|
||||
return classVal.join(' ');
|
||||
} else if (isObject(classVal)) {
|
||||
var classes = [], i = 0;
|
||||
forEach(classVal, function(v, k) {
|
||||
if (v) {
|
||||
classes.push(k);
|
||||
}
|
||||
});
|
||||
return classes.join(' ');
|
||||
}
|
||||
|
||||
return classVal;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function arrayDifference(tokens1, tokens2) {
|
||||
var values = [];
|
||||
|
||||
outer:
|
||||
for(var i = 0; i < tokens1.length; i++) {
|
||||
var token = tokens1[i];
|
||||
for(var j = 0; j < tokens2.length; j++) {
|
||||
if(token == tokens2[j]) continue outer;
|
||||
}
|
||||
values.push(token);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function arrayClasses (classVal) {
|
||||
if (isArray(classVal)) {
|
||||
return classVal;
|
||||
} else if (isString(classVal)) {
|
||||
return classVal.split(' ');
|
||||
} else if (isObject(classVal)) {
|
||||
var classes = [], i = 0;
|
||||
forEach(classVal, function(v, k) {
|
||||
if (v) {
|
||||
classes.push(k);
|
||||
}
|
||||
});
|
||||
return classes;
|
||||
}
|
||||
return classVal;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -192,6 +192,19 @@ describe('ngClass', function() {
|
||||
}));
|
||||
|
||||
|
||||
it("should allow ngClassOdd/Even on the same element with overlapping classes", inject(function($rootScope, $compile, $animate) {
|
||||
var className;
|
||||
|
||||
element = $compile('<ul><li ng-repeat="i in [0,1,2]" ng-class-odd="\'same odd\'" ng-class-even="\'same even\'"></li><ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[5]);
|
||||
expect(e1.hasClass('same')).toBeTruthy();
|
||||
expect(e1.hasClass('odd')).toBeTruthy();
|
||||
expect(e2.hasClass('same')).toBeTruthy();
|
||||
expect(e2.hasClass('odd')).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should allow both ngClass and ngClassOdd/Even with multiple classes', inject(function($rootScope, $compile) {
|
||||
element = $compile('<ul>' +
|
||||
'<li ng-repeat="i in [0,1]" ng-class="[\'A\', \'B\']" ' +
|
||||
|
||||
Reference in New Issue
Block a user