搞定angular1.x——複雜指令

stavencsdn發表於2016-09-21

名稱

描述

compile

指定一個編譯函式

controller

為指令建立一個控制器函式

link

為指令指定連結函式

replace

指定模板內容是否替換指令所應用到的元素

require

宣告對某個控制器的依賴

restrict

指定指令如何使用ACEM

scope

為指令建立一個新的作用域或者一個隔離的作用域

template

指定一個將被插入到HTML文件的模板

templateUrl

指定一個將被插入到HTML文件的外部模板

transclude

指定指令是否被用於包含任意內容

 

  1. .directive('unorderedList', function () {
  2.     return {
  3.         link: function (scope, element, attrs) {
  4.             var data = scope[attrs['unorderedList'] || attrs['listSource'] ];
  5.             var propertyName = attrs['listProperty'] || "price || currency";
  6.             if(angular.isArray(data)){
  7.                 var listElem = angular.element("<ul>");
  8.                 if(element[0].nodeName == "#comment"){
  9.                     element.parent().append(listElem);
  10.                 }else{
  11.                     element.append(listElem);
  12.                 }
  13.                 for(var i=0, len=data.length; i<len; i++){
  14.                     var itemElem = angular.element('<li>').text(scope.$eval(propertyName, data[i]));
  15.                     listElem.append(itemElem);
  16.                 }
  17.             }
  18.         },
  19.         restrict:'EACM'
  20.     };
  21. });

如何使用指令

當作元素使用(E)

  1. <unordered-list list-source="products" list-property="price | currency" />

        當unordered-list當作元素使用,需要新增另外的屬性代替unordered-list屬性的作用。

  1. var data = scope[attrs['unorderedList'] || attrs['listSource'] ];

當作屬性使用(A)

  1. <div unordered-list="products" list-property="price | currency"></div>

當作類的屬性值使用(C)

  1. <div class="unordered-list: products" list-property="price | currency"></div>

當作註釋使用(M)

  1. <!-- directive: unordered-list products  -->

使用模板指令

  1. .directive('unorderedList', function () {
  2.     return {
  3.         link: function (scope, element, attrs) {
  4.             scope.data = scope[attrs['unorderedList']];
  5.         },
  6.         restrict: 'A',
  7.         template:"<ul><li ng-repeat='item in data'>{{item.price | currency}}</li></ul>"
  8.     };
  9. });

使用函式作為模板

        template屬性除了使用字串,也可以指定一個函式來生成模板化的內容。該函式傳入兩個函式(指令所應用到的元素以及屬性集合)並返回將被插入到文件中的HTML程式碼片段。

  1. <script type="text/javascript" id="listTemplate">
  2.     <ul>
  3.         <li ng-repeat="item in data">{{item.price | currency}}</li>
  4.     </ul>
  5. </script>
  6. <script>
  7. var myApp = angular.module('myApp', [])
  8.  
  9.     .controller('myCtrl', ["$scope", function ($scope) {
  10.         $scope.products = [
  11.             { name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
  12.             { name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
  13.             { name: "Pears", category: "Fruit", price: 2.02, expiry: 6 }
  14.         ];
  15.     }])
  16.         .directive('unorderedList', function () {
  17.             return {
  18.                 link: function (scope, element, attrs) {
  19.                     scope.data = scope[attrs['unorderedList']];
  20.                 },
  21.                 restrict: 'A',
  22.                 template:function () {
  23.                     return angular.element(
  24.                             document.querySelector("#listTemplate")
  25.                     ).html();
  26.                 }
  27.             };
  28.         });
  29. </script>

使用外部模板

itemTemplate.html

  1. <p>This is the form the template file</p>
  2. <ul>
  3.     <li ng-repeat="item in data">{{item.price | currency}}</li>
  4. </ul>
  1. .directive('unorderedList', function () {
  2.     return {
  3.         link: function (scope, element, attrs) {
  4.             scope.data = scope[attrs['unorderedList']];
  5.         },
  6.         restrict: 'A',
  7.         templateUrl:"itemTemplate.html"
  8.     };
  9. });

通過函式選擇一個外部模版

tableTemplate.html

  1. <table>
  2.     <thead>
  3.         <tr>
  4.             <th>Name</th>
  5.             <th>Price</th>
  6.         </tr>
  7.     </thead>
  8.     <tbody>
  9.         <tr ng-repeat="item in data">
  10.             <td>{{item.name}}</td>
  11.             <td>{{item.price | currency}}</td>
  12.         </tr>
  13.     </tbody>
  14. </table>
  1. <div unordered-list="products" template="table" class="table table-striped">
  2.     This is where the list will go
  3. </div>
  1. .directive('unorderedList', function () {
  2.     return {
  3.         link: function (scope, element, attrs) {
  4.             scope.data = scope[attrs['unorderedList']];
  5.         },
  6.         restrict: 'A',
  7.         templateUrl: function (elem, attrs) {
  8.             return attrs['template'] == "table" ? "tableTemplate.html" : "itemTemplate.html";
  9.         }
  10.     };
  11. });

        table-striped樣式並沒有起作用,設定replace屬性為true後的效果是模版內容將替換掉指令所應用到的div元素。

管理指令的作用域

為每個指令例項建立自己的作用域

        設定scope屬性為true將允許我們在同一個控制器裡複用這個指令,可以避免指令共享資料值。

  1. <div class="panel panel-default">
  2.    <div  class="panel-body" scope-demo></div>
  3.     <div  class="panel-body" scope-demo></div>
  4. </div>
  1. var myApp = angular.module('myApp', [])
  2. .controller('myCtrl', ["$scope", function ($scope) {
  3.     $scope.data = {name:"Staven"};
  4.     $scope.city = "China"
  5. }])
  6. .directive('scopeDemo', function () {
  7.     return {
  8.        template: function () {
  9.            return angular.element(document.querySelector("#scopeTemplate")).html();
  10.        },
  11.         scope:true
  12.     };
  13. });

        data.name這個屬性是在一個物件上定義的,意味著這個值將會在指令的哥哥例項之間所共享,而且所有相應的檢視會同步更新。

        city是直接在控制器的作用於上被直接賦值的,意味著這個值只在此指令的作用域上有效。

建立隔離的作用域

        對於在一個物件上定義的屬性,可能會被其他人改變。解決方法就是建立一個隔離的作用域,就是Angularjs為指令的每個例項建立一個獨立的作用域的地方,但是這個作用域並不繼承自控制器的作用域。當scope定義屬性被設定為一個物件時,可建立一個隔離的作用域。隔離的作用域的最基本型別是用一個沒有屬性的物件表示。

  1. .directive('scopeDemo', function () {
  2.     return {
  3.        template: function () {
  4.            return angular.element(document.querySelector("#scopeTemplate")).html();
  5.        },
  6.         scope:{}
  7.     };
  8. });

        當建立在不同情況下複用的指令時,隔離的作用域是一種重要的構件時。因為它防止了在控制器作用域和指令之間出現了意料外的互動。但是完全隔絕一個指令會使得難以輸入和輸出資料。

        隔絕的作用域允許使用應用於指令旁邊的元素上的屬性將資料值繫結到控制器作用域上。

單向繫結@:

        向以@為字首的作用域物件上增添一個屬性,以在一個隔離的作用力建立一個單向繫結。

  1. <body ng-app="myApp" ng-controller="myCtrl">
  2.     <div class="panel panel-default">
  3.        <div class="panel-body">
  4.            Direct Binding:<input ng-model="data.name" />
  5.        </div>
  6.         <div class="panel-body" scope-demo nameprop="{{data.name}}"></div>
  7.     </div>
  8. </body>
  9. <script type="text/ng-template" id="scopeTemplate">
  10.     <div class="panel-body">
  11.         <p>Data Value:{{local}}</p>
  12.     </div>
  13. </script>
  14. <script>
  15. var myApp = angular.module('myApp', [])
  16. .controller('myCtrl', ["$scope", function ($scope) {
  17.     $scope.data = {name:"staven"};
  18. }])
  19. .directive('scopeDemo', function () {
  20.     return {
  21.        template: function () {
  22.            return angular.element(document.querySelector("#scopeTemplate")).html();
  23.        },
  24.         scope:{
  25.            local:"@nameprop"
  26.         }
  27.     };
  28. });
  29. </script> 

        local屬性的值以@為字首,制定了屬性local的值應該從一個來自名為nameprop的特性的單向繫結來獲得。

        使用一個隔離的作用域使得指令不會繼承控制器作用域中的資料。

雙向繫結=:

        向以=為字首的作用域物件上增添一個屬性,以在一個隔離的作用域裡建立一個雙向繫結。

        在隔離作用於上的單向繫結總是被計算作字串值,如果想訪問一個陣列,就必須使用雙向繫結。

  1. <div class="panel panel-default">
  2.    <div class="panel-body">
  3.        Direct Binding:<input ng-model="data.name" />
  4.    </div>
  5.     <div class="panel-body" scope-demo nameprop="data.name"></div>
  6. </div>
  1. <script type="text/ng-template" id="scopeTemplate">
  2.     <div class="panel-body">
  3.         <p>Data Value:<input ng-model="local" /></p>
  4.     </div>
  5. </script>
  6. <script>
  7.     scope:{
  8.        local:"=nameprop"
  9.     }
  10. </script>

        使用單向繫結時,提供了一個被"{{"和"}}"字元所包圍的繫結表示式,但是angularjs需要知道在雙向繫結中哪個屬性需要被更新,所以不需要被"{{"和"}}"包圍。

計算表示式&:

        向以&為字首的作用域物件上增添一個屬性,在父作用域的上下文計算一個表示式。

  1. <body ng-app="myApp" ng-controller="myCtrl">
  2. <div class="panel panel-default">
  3.    <div class="panel-body">
  4.        Direct Binding:<input ng-model="data.name" />
  5.    </div>
  6.     <div class="panel-body" scope-demo city="getCity(data.name)" nameprop="data.name"></div>
  7. </div>
  8. </body>
  9. <script type="text/ng-template" id="scopeTemplate">
  10.     <div class="panel-body">
  11.         <p>Name:{{local}}, City:{{cityFn()}}</p>
  12.     </div>
  13. </script>
  14. <script>
  15. var myApp = angular.module('myApp', [])
  16. .controller('myCtrl', ["$scope", function ($scope) {
  17.     $scope.data = {name:"staven",defaultCity:"hefei"};
  18.     $scope.getCity = function (name) {
  19.         console.log(1);
  20.         return name == 'staven' ? $scope.data.defaultCity : "Unknown";
  21.     }
  22. }])
  23. .directive('scopeDemo', function () {
  24.     return {
  25.        template: function () {
  26.            return angular.element(document.querySelector("#scopeTemplate")).html();
  27.        },
  28. scope:{
  29.     local:"=nameprop",
  30.     cityFn:"&city"
  31. }
  32.     };
  33. });
  34. </script>

        呼叫cityFn()時,使用了圓括號,要計算被這個特性所指定的表示式,這是必需的,即使當表示式本身就是一個函式呼叫時。

使用隔離作用域的資料來計算一個表示式

        可以將來自代計算的隔離作用域的資料為控制器作用域表示式的一部分。

  1. <div class="panel-body" scope-demo city="getCity(nameVal)" nameprop="data.name"></div>
  1. <script type="text/ng-template" id="scopeTemplate">
  2.     <div class="panel-body">
  3.         <p>Name:{{local}}, City:{{cityFn({nameVal: local})}}</p>
  4.     </div>
  5. </script>

相關文章