ng-class指令

概述

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配合使用,控制奇偶行顯示不同樣式的功能。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

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