ng-class指令

概述

ng-class指令實現(xiàn)了把元素的class屬性和數(shù)據(jù)模型綁定的功能。在進(jìn)行樣式動態(tài)控制時非常的有用。
ng-class指令實際上有三種。分別為:

指令名稱 指令說明
ng-class 在任何元素中都可以使用
ng-class-odd 配合ng-repeat指令使用,$index為偶數(shù)時生效
ng-class-even 配合ng-repeat指令使用,$index為奇數(shù)時生效

實現(xiàn)細(xì)節(jié)

三種指令實現(xiàn)上幾乎是一模一樣的,ng-class-odd和ng-class-even多了一個對$index數(shù)據(jù)的監(jiān)控。
核心代碼:

響應(yīng)數(shù)據(jù)變化

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);
  }
}

這個函數(shù)是在監(jiān)控到ng-class(ng-class-odd或ng-class-even)的值發(fā)生變化時調(diào)用的。在這個函數(shù)中主要進(jìn)行了三個事情:
1、根據(jù)新值獲取到需要添加的class名稱列表。
2、添加或者更新class列表。
3、保存當(dāng)前的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

當(dāng)綁定的數(shù)據(jù)從無變化為有數(shù)據(jù)的時候會直接調(diào)用添加class的方法,代碼為:

function removeClasses(classes) {
  var newClasses = digestClassCounts(classes, -1);
  attr.$removeClass(newClasses);
}

這個方法比較簡單,只有一個digestClassCounts方法需要注意,在后面會詳細(xì)說明。

更新class

當(dāng)綁定的數(shù)據(jù)從一個值變化到令一個值的時候,就會調(diào)用更新class的方法,這個過程只會修改變動的class,其它c(diǎn)lass不會修改。

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、去除重復(fù)的class。
3、添加需要添加的class,刪除需要刪除的class。
arrayDifference方法主要是計算出第一個參數(shù)相對與第二個參數(shù)多出來的Array元素,內(nèi)部為循環(huán)遍歷,比較簡單。

class去除重復(fù)

在實際使用的過程中,很有可能會出現(xiàn)多個綁定的數(shù)據(jù)的值是一樣的,導(dǎo)致得到的class列表里面會有重復(fù)的值,這個時候為了提高效率,就需要提前去除重復(fù)的數(shù)據(jù)。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時參數(shù)count就等于1,刪除class時count就等于-1。通過遍歷以及之前緩存的每一個class的次數(shù)來判斷是否需要添加到最終的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會去監(jiān)測$index數(shù)據(jù),$index數(shù)據(jù)是ng-repeat的一個默認(rèn)數(shù)據(jù),所以這兩個指令更多是配合ng-repeat使用,當(dāng)然理論是自己控制$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>

在這段代碼中實現(xiàn)了使用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>

這段代碼實現(xiàn)了ng-class-odd和ng-class-even與ng-repeat配合使用,控制奇偶行顯示不同樣式的功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.背景介紹 指令是一個Dom元素上的標(biāo)簽(和元素上的屬性, CSS類樣式一樣,屬于這個Dom元素)它告訴Angu...
    cczhuc閱讀 1,316評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • ng-class用于動態(tài)綁定標(biāo)簽class 用例: <input type=“number” name...
    報告老師閱讀 230評論 0 0
  • AngularJS是什么?AngularJs(后面就簡稱ng了)是一個用于設(shè)計動態(tài)web應(yīng)用的結(jié)構(gòu)框架。首先,它是...
    200813閱讀 1,760評論 0 3
  • 第一天 是粉色的傍晚 新的生活 粉粉嫩嫩開場啦
    哈希算法閱讀 276評論 0 0

友情鏈接更多精彩內(nèi)容