概述
ng-class指令實現了把元素的class屬性和數據模型綁定的功能。在進行樣式動態控制時非常的有用。
ng-class指令實際上有三種。分別為:
指令名稱 | 指令說明 |
---|---|
ng-class | 在任何元素中都可以使用 |
ng-class-odd | 配合ng-repeat指令使用,$index為偶數時生效 |
ng-class-even | 配合ng-repeat指令使用,$index為奇數時生效 |
實現細節
三種指令實現上幾乎是一模一樣的,ng-class-odd和ng-class-even多了一個對$index數據的監控。
核心代碼:
響應數據變化
function ngClassWatchAction(newVal) {
// eslint-disable-next-line no-bitwise
if (selector === true || (scope.$index & 1) === selector) {
var newClasses = arrayClasses(newVal || []);
if (!oldVal) {
addClasses(newClasses);
} else if (!equals(newVal,oldVal)) {
var oldClasses = arrayClasses(oldVal);
updateClasses(oldClasses, newClasses);
}
}
if (isArray(newVal)) {
oldVal = newVal.map(function(v) { return shallowCopy(v); });
} else {
oldVal = shallowCopy(newVal);
}
}
這個函數是在監控到ng-class(ng-class-odd或ng-class-even)的值發生變化時調用的。在這個函數中主要進行了三個事情:
1、根據新值獲取到需要添加的class名稱列表。
2、添加或者更新class列表。
3、保存當前的ng-class的值備用。
其中第一步和第二步是核心。
獲取需要添加的class列表
function arrayClasses(classVal) {
var classes = [];
if (isArray(classVal)) {
forEach(classVal, function(v) {
classes = classes.concat(arrayClasses(v));
});
return classes;
} else if (isString(classVal)) {
return classVal.split(' ');
} else if (isObject(classVal)) {
forEach(classVal, function(v, k) {
if (v) {
classes = classes.concat(k.split(' '));
}
});
return classes;
}
return classVal;
}
從代碼可以看出,ng-class的值支持三種形式:
類型 | 說明 |
---|---|
Array | 每一個元素可以為string,Array,Object |
string | 多個class以空格分隔,每一個class會被直接加入class列表 |
Object | 會遍歷Object的屬性,只有屬性為true的才會被添加到class列表 |
添加class
當綁定的數據從無變化為有數據的時候會直接調用添加class的方法,代碼為:
function removeClasses(classes) {
var newClasses = digestClassCounts(classes, -1);
attr.$removeClass(newClasses);
}
這個方法比較簡單,只有一個digestClassCounts方法需要注意,在后面會詳細說明。
更新class
當綁定的數據從一個值變化到令一個值的時候,就會調用更新class的方法,這個過程只會修改變動的class,其它class不會修改。
function updateClasses(oldClasses, newClasses) {
var toAdd = arrayDifference(newClasses, oldClasses);
var toRemove = arrayDifference(oldClasses, newClasses);
toAdd = digestClassCounts(toAdd, 1);
toRemove = digestClassCounts(toRemove, -1);
if (toAdd && toAdd.length) {
$animate.addClass(element, toAdd);
}
if (toRemove && toRemove.length) {
$animate.removeClass(element, toRemove);
}
}
這個方法主要干的事情有:
1、獲取需要添加和需要刪除的class。
2、去除重復的class。
3、添加需要添加的class,刪除需要刪除的class。
arrayDifference方法主要是計算出第一個參數相對與第二個參數多出來的Array元素,內部為循環遍歷,比較簡單。
class去除重復
在實際使用的過程中,很有可能會出現多個綁定的數據的值是一樣的,導致得到的class列表里面會有重復的值,這個時候為了提高效率,就需要提前去除重復的數據。digestClassCounts方法就是起到了這個作用。
function digestClassCounts(classes, count) {
// Use createMap() to prevent class assumptions involving property
// names in Object.prototype
var classCounts = element.data('$classCounts') || createMap();
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(' ');
}
添加class時參數count就等于1,刪除class時count就等于-1。通過遍歷以及之前緩存的每一個class的次數來判斷是否需要添加到最終的class列表。
ng-class-odd和ng-class-even特有的處理過程
if (name !== 'ngClass') {
scope.$watch('$index', function($index, old$index) {
/* eslint-disable no-bitwise */
var mod = $index & 1;
if (mod !== (old$index & 1)) {
var classes = arrayClasses(scope.$eval(attr[name]));
if (mod === selector) {
addClasses(classes);
} else {
removeClasses(classes);
}
}
/* eslint-enable */
});
}
從代碼可以看出,對于ng-class-odd和ng-class-even會去監測$index數據,$index數據是ng-repeat的一個默認數據,所以這兩個指令更多是配合ng-repeat使用,當然理論是自己控制$index的值來使用這個兩個指令也是可以的。
樣例代碼
ng-class的樣例代碼
<!DOCTYPE html>
<html lang="en" ng-app="app">
<!--<html>-->
<head>
<title>Test</title>
<style>
.strike {
text-decoration: line-through;
}
.bold {
font-weight: bold;
}
.red {
color: red;
}
.has-error {
color: red;
background-color: yellow;
}
.orange {
color: orange;
}
</style>
</head>
<body>
<p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
<label>
<input type="checkbox" ng-model="deleted">
deleted (apply "strike" class)
</label><br>
<label>
<input type="checkbox" ng-model="important">
important (apply "bold" class)
</label><br>
<label>
<input type="checkbox" ng-model="error">
error (apply "has-error" class)
</label>
<hr>
<p ng-class="style">Using String Syntax</p>
<input type="text" ng-model="style"
placeholder="Type: bold strike red" aria-label="Type: bold strike red">
<hr>
<p ng-class="[style1, style2, style3]">Using Array Syntax</p>
<input ng-model="style1"
placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
<input ng-model="style2"
placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
<input ng-model="style3"
placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
<hr>
<p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
<input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
<label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
</body>
<script src="./node_modules/angular/angular.js" type="text/javascript"></script>
<script src="./node_modules/angular-sanitize/angular-sanitize.js" type="text/javascript"></script>
<script>
angular.module('app', [])
.controller('ExampleController', ['$scope', function ($scope) {
}]);
</script>
</html>
在這段代碼中實現了使用ng-class來控制元素樣式的工程,ng-class多種綁定方式都有。
ng-class-odd和ng-class-even樣例代碼
<!DOCTYPE html>
<html lang="en" ng-app="app">
<!--<html>-->
<head>
<title>Test</title>
<style>
.odd {
color: red;
}
.even {
color: blue;
}
</style>
</head>
<body>
<ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
<li ng-repeat="name in names">
<span ng-class-odd="'odd'" ng-class-even="'even'">
{{name}}
</span>
</li>
</ol>
</body>
<script src="./node_modules/angular/angular.js" type="text/javascript"></script>
<script src="./node_modules/angular-sanitize/angular-sanitize.js" type="text/javascript"></script>
<script>
angular.module('app', [])
.controller('ExampleController', ['$scope', function ($scope) {
}]);
</script>
</html>
這段代碼實現了ng-class-odd和ng-class-even與ng-repeat配合使用,控制奇偶行顯示不同樣式的功能。