AngularJS 講解,四 Directive

AngularJS  Directive 自定義指令(我最喜歡AngularJs的功能之一)javascript

一:何時咱們會用到directivecss

    1.使html更具語義化,不用深刻了解研究代碼的邏輯即可知道大體邏輯。html

  2.抽象出一個自定義組件,能夠重複使用。java

二:directive的定義及其使用方法angularjs

    1.下面是一個directive參數詳細模板express

angular.module('app',[]);//申明一個調用angularjs塊 
  
angular.module('app').directive('directiveName', function factory() { 
  
 var directiveDefinitionObject = { 
   priority: 0,
   restrict:'A',   template: '<div></div>',   templateUrl: 'directive.html',   replace: false,   transclude: false,   scope: false,   compile: function compile(tElement, tAttrs, transclude) {     return {       pre: function preLink(scope, iElement, iAttrs, controller) { ... },       post: function postLink(scope, iElement, iAttrs, controller) { ... }     }   },   link: function postLink(scope, iElement, iAttrs) { ... } }; return directiveDefinitionObject; });

  2.參數詳解數組

       priority:緩存

    (數字),可選參數,指明指令的優先級,若在單個DOM上有多個指令,則優先級高的先執行;安全

   terminal:app

    (布爾型),可選參數,值爲true或false,若設置爲true,則優先級低於此指令的其餘指令則無效,不會被調用(優先級相同的仍是會執行);

      restrict:

          (字符串)可選參數,指明指令在DOM裏面以什麼形式被聲明;

    取值有:E(元素),A(屬性),C(類),M(註釋),其中默認值爲A;固然也能夠兩個一塊兒用,好比EA.表示便可以是元素也能夠是屬性。

    E(元素):<directiveName></directiveName>  
    A(屬性):<div directiveName='expression'></div>  
    C(類): <div class='directiveName'></div>  
    M(註釋):<--directive:directiveName expression-->  
    通常狀況下E/A/C用得比較多。

  template:

    (字符串或函數),可選參數

    例:(1)字符串時:  

<!DOCTYPE html> 
<html lang="zh" ng-app="app"> 
<head> 
 <meta charset="UTF-8"> 
 <title>AngularJS Directive</title> 
 <script type="text/javascript" src="angular.min.js"></script> 
</head> 
<body> 
<hello-world></hello-world> 
</body> 
<script type="text/javascript"> 
angular.module('app', []); 
angular.module.('app').directive('helloWorld', function() { 
 return { 
 restrict: 'E', 
 template: '<div><h1>Hi I am hello world.</h1></div>', 
 replace: true 
 }; 
}); 
</script> 
</html> 

   (2)函數時:有兩個參數tElement和tAttrs

      tElement:使用此指令的元素

        tAttrs:該指令元素上的屬性

<!DOCTYPE html> 
<html lang="zh" ng-app="app"> 
<head> 
 <meta charset="UTF-8"> 
 <title>AngularJS Directive</title> 
 <script type="text/javascript" src="angular.min.js"></script> 
</head> 
<body> 
<hello-world></hello-world> 
<hello-world2 title = 'I am a Hello World 2.'></hello-world2> 
</body> 
<script type="text/javascript"> 
angular.module('app', []); 
app.directive('helloWorld', function() { 
 return { 
 restrict: 'E', 
 template: '<div><h1>Hi I am a Hello Worl.</h1></div>', 
 replace: true 
 }; 
}); 
app.directive("helloWorld2",function(){ 
 return{ 
 restrict:'EAC', 
 template: function(tElement,tAttrs){ 
 var _html = ''; 
 _html += '<div>'+tAttrs.title+'</div>'; 
 return _html; 
 } 
 }; 
 }); 
</script> 
</html> 

  templateUrl:

    (字符串或者函數)可選參數

    字符串:表明HTML文件路徑的字符串

    函數:可接收兩個參數tElement和tAttrs(大體同上)

    (PS:因爲加載html模板是經過異步加載的,若加載大量的模板會拖慢網站的速度,因此咱們能夠先緩存模板)

    例:(把要加載的頁面 先加載好 包含在你須要的html裏)    

<!DOCTYPE html> 
<html lang="zh" ng-app="app"> 
<head> 
 <meta charset="UTF-8"> 
 <title>AngularJS Directive</title> 
 <script type="text/javascript" src="angular.min.js"></script> 
</head> 
<body> 
<hello-world></hello-world> 
</body> 
<script type="text/javascript"> 
angular.module('app', []); 
angular.module("app").directive('helloWorld', function() { 
 return { 
 restrict: 'E', 
 templateUrl: 'hello.html', 
 replace: true 
 }; 
}); 
</script> 
<script type='text/ng-template' id='hello.html'> 
 <div><h1>Hi I am Hello world.</h1></div> 
</script> 
</html> 

 還有一種方法:

angular.module("app").run(["$templateCache", function($templateCache) { 
 $templateCache.put("hello.html", 
 "<div><h1>Hi I am Hello world.</h1></div>"); 
}]); 

  replace:

    (布爾值):默認值是false. true:替換掉指令即(<hello-world></hello-world>),false:不替換

  transclude:(是否想要指令內部的內容被模板替換掉)

    (布爾值):默認值是false. true:須要和ng transclude一塊兒使用

    例:template:"<div>hello every <div ng-transclude></div></div>"

        這時,指令內部的內容會嵌入到ng-transclude這個div中。也就是變成了<div>hello every <div>這是指令內部的內容</div></div>。

  scope:

    1)默認值false。表示繼承父做用域;(繼承不隔離)

    2)true。表示繼承父做用域,並建立本身的做用域(子做用域);(繼承隔離)

    3){}。表示建立一個全新的隔離做用域;(不繼承隔離)

    (PS:當你想要建立一個可重用的組件時隔離做用域是一個很好的選擇,經過隔離做用域咱們確保指令是‘獨立'的,並能夠輕鬆地插入到任何HTML app中,而且這種作法防止了父做用域被污染;)

    下面針對隔離做用域進行詳細講解:

       a.隔離做用域怎樣去訪問父做用域:

       Directive 在使用隔離 scope 的時候,提供了三種方法同隔離以外的地方交互.

      1). @ 

        (用來訪問 directive 外部環境定義的字符串值,主要是經過 directive 所在的標籤屬性綁定外部字符串值。這種綁定是單向的,即父 scope 的綁定變化,directive 中的 scope 的屬性會同步變化,                                     而隔離 scope 中的綁定變化,父 scope 是不知道的。)PS:至關於繼承隔離

        scope:{

        name:"@"

        }     

      2). &

        (& 方式提供一種途經是 directive 能在父 scope 的上下文中執行一個表達式。當 directive 中有什麼動做須要更新到父 scope 中的時候,能夠在父 scope 上下文中執行一段代碼或者一個函數。PS:即綁定的是方法)

        

<!DOCTYPE html> 
<html lang="zh" ng-app="app"> 
<head> 
 <meta charset="UTF-8"> 
 <title>AngularJS Directive</title> 
 <script type="text/javascript" src="angular.min.js"></script> 
</head> 
<body> 
 <div ng-controller="myController"> 
 <div>父scope: 
 <div>Say:{{value}}</div> 
 </div> 
 <div>隔離scope: 
 <div isolated-directive action="click()"></div> 
 </div> 
</div> 
</body> 
<script type="text/javascript"> 
angular.module('app', []); 
app.controller("myController", function ($scope) { 
 $scope.value = "hello world"; 
 $scope.click = function () { 
 $scope.value = Math.random(); 
 }; 
 }).directive("isolatedDirective", function () { 
 return { 
 scope: { 
 action: "&" 
 }, 
 template: '<input type="button" value="在directive中執行父scope定義的方法" ng-click="action()"/>' 
 } 
 }) 
</script> 
</html> 

 

      3). =

       (經過 directive 的 attr 屬性的值在局部 scope 的屬性和父 scope 屬性名之間創建雙向綁定。
        意思是,當你想要一個雙向綁定的屬性的時候,你可使用=來引入外部屬性。不管是改變父 scope 仍是隔離 scope 裏的屬性,父 scope 和隔離 scope 都會同時更新屬性值,由於它們是雙向綁定的關係。)

      例:

<!DOCTYPE html> 
<html lang="zh" ng-app="app"> 
<head> 
 <meta charset="UTF-8"> 
 <title>AngularJS Directive</title> 
 <script type="text/javascript" src="angular.min.js"></script> 
</head> 
<body> 
<div ng-controller="myController"> 
 <div>父scope: 
 <div>Say:{{user.name}}<br>改變父scope的name:<input type="text" value="" ng-model="userBase.name"/></div> 
 </div> 
 <div>隔離scope: 
 <div isolated-directive user="userBase"></div> 
 </div> 
</div> 
</body> 
<script type="text/javascript"> 
angular.module('app', []); 
angular.module.("app").controller("myController", function ($scope) { 
 $scope.userBase = { 
 name: 'hello', 
 id: 1 
 }; 
 }).directive("isolatedDirective", function () { 
 return { 
 scope: { 
 user: "=" 
 }, 
 template: 'Say:{{user.name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="user.name"/>' 
 } 
 }) 
</script> 
</html> 

   controller:

    (能夠是一個字符串或者函數)

    

angular.module('app', []) 
angular.module.("app").directive('myDirective', function() { 
restrict: 'A', 
controller: 'DefinitionController' 
}) 
// 應用中其餘的地方,能夠是同一個文件或被index.html包含的另外一個文件 
angular.module('app') 
.controller('DefinitionController', function($scope, $element, $a
也能夠直接在指令內部的定義爲匿名函數,一樣咱們能夠再這裏注入任何服務($log,$timeout等等)
 
js:
angular.module('app',[]) 
.directive('myDirective', function() { 
restrict: 'A', 
controller: 
function($scope, $element, $attrs, $transclude) { 
// 控制器邏輯放在這裏 
} 
}); 

另外還有一些特殊的服務(參數)能夠注入

(1)$scope,與指令元素相關聯的做用域

(2)$element,當前指令對應的 元素

(3)$attrs,由當前元素的屬性組成的對象

(4)$transclude,嵌入連接函數,實際被執行用來克隆元素和操做DOM的函數

注意: 除非是用來定義一些可複用的行爲,通常不推薦在這使用。
         指令的控制器和link函數(後面會講)能夠進行互換。區別在於,控制器主要是用來提供可在指令間複用的行爲但link連接函數只能在當前內部指令中定義行爲,且沒法再指令間複用。

   

<!DOCTYPE html> 
<html lang="zh" ng-app="app"> 
<head> 
 <meta charset="UTF-8"> 
 <title>AngularJS入門學習</title> 
 <script type="text/javascript" src="angular.min.js"></script> 
</head> 
 <hello mycolor ="red">I am Hello World.</hello> 
</body> 
<script type="text/javascript"> 
angular.module('app', []); 
angular.module("app").directive('hello', function() { 
 return { 
  restrict: 'EA', 
  transclude: true, //注意此處必須設置爲true 
  controller: 
  function ($scope, $element,$attrs,$transclude,$log) { //在這裏你能夠注入你想注入的服務 
  $transclude(function (clone) { 
   var a = angular.element('<p>'); 
   a.css('color', $attrs.mycolor); 
   a.text(clone.text()); 
   $element.append(a); 
  }); 
  $log.info("hello everyone"); 
  } 
 }; 
 }); 
</script> 
</html> 

   這裏若是咱們想要實用的父做用域:$scope.$parent

                               新的做用域:$scope.$parent.new()

  controllerAs 

    設置控制器的別名(angular1.2給咱們帶來的新的語法糖)

           例:

<script> 
 angular.module('app',[]).directive('myDirective', function () { 
 return { 
  restrict: 'EA', 
  transclude: true, 
  controller:'someController', 
  controllerAs:'mainController'
  //..其餘配置 
 }; 
 }); 
 </script> 

   require

    字符串或數組

      字符串表明另外一個指令的名字,做爲link函數的第四個參數。假設如今咱們要編寫兩個指令,兩個指令中的link連接函數中存在有不少重合的方法,這時候咱們就能夠將這些重複的方法寫在第三個指令的controller中(上面也講到controller常常用來提供指令間的複用行爲)而後在這兩個指令中,require這個擁有controller字段的的指令(第三個指令),最後經過link連接函數的第四個參數就能夠引用這些重合的方法了。

    例

<!doctype html> 
<html ng-app="app"> 
<head> 
 <script src="angular.min.js"></script> 
</head> 
<body>   
 <outer-directive> 
 <inner-directive></inner-directive> 
 <inner-directive2></inner-directive2> 
 </outer-directive> 
 <script> 
 angular.module('app', []); 
 angular.module("app").directive('outerDirective', function() { 
  return { 
  scope: {}, 
  restrict: 'AE', 
  controller: function($scope) { 
   this.say = function(someDirective) { 
   console.log('Show:' + someDirective.message); 
   }; 
  } 
  }; 
 }); 
 angular.module("app").directive('innerDirective', function() { 
  return { 
  scope: {}, 
  restrict: 'AE', 
  require: '^outerDirective', 
  link: function(scope, elem, attrs, controllerInstance) { 
   scope.message = "Hi,I am Ruby."; 
   controllerInstance.say(scope); 
  } 
  }; 
 }); 
 app.directive('innerDirective2', function() { 
  return { 
  scope: {}, 
  restrict: 'AE', 
  require: '^outerDirective', 
  link: function(scope, elem, attrs, controllerInstance) { 
   scope.message = "Hi,I am Hello World."; 
   controllerInstance.say(scope); 
  } 
  }; 
 }); 
  
  
 </script> 
  
</body> 
</html> 

     require參數有四種:   

      1)沒有前綴,指令會在自身提供的控制器中進行查找,若是找不到任何控制器,則會拋出一個error

      2)?若是在當前的指令沒有找到所需的控制器,則會將null傳給link鏈接函數的第四個參數

      3)^若是在當前的指令沒有找到所需的控制器,則會查找父元素的控制器

      4)?^組合

  compile function

    function compile(tElement, tAttrs, transclude) { ... }

    編譯函數用來修改模板dom的(不經常使用)

    須要用到編譯函數例如:ngTrepeat;ngView(須要異步載入內容的)。

    各參數意義:

    tElement - template element 該指令所在的元素。

      tAttrs - template attributes 指令元素上所聲明的屬性。

    transclude - 一個嵌入的連接函數function(scope, cloneLinkingFn)。

    注意:在編譯函數裏面不要進行任何DOM變形以外的操做。 更重要的,DOM監聽事件的註冊應該在連接函數中作,而不是編譯函數中。 編譯函數能夠返回一個對象或者函數。 返回函數 - 等效於在編譯函數不存在時,使用配置對象的link屬性註冊的連接函數。 返回對象 - 返回一個經過pre或post屬性註冊了函數的對象。參考下面pre-linking和post-liking函數的解釋。

  link function

    function link(scope, iElement, iAttrs, controller) { ... }
    連接函數負責註冊DOM事件和更新DOM。它是在模板被克隆以後執行的,它也是大部分指令邏輯代碼編寫的地方。
    scope - 指令須要監聽的做用域。
    iElement - instance element - 指令所在的元素。只有在postLink函數中對元素的子元素進行操做纔是安全的,由於那時它們才已經所有連接好。
    iAttrs - instance attributes - 實例屬性,一個標準化的、全部聲明在當前元素上的屬性列表,這些屬性在全部連接函數間是共享的。
    controller - 控制器實例,也就是當前指令經過require請求的指令someDirective內部的controller。好比:someDirective指令中的controller:function(){this.Say = function(){}},那麼,在當前指令的link函數中,你就能夠經過controller.Say進行調用了。
    Pre-linking function 在子元素被連接前執行。不能用來進行DOM的變形,以防連接函數找不到正確的元素來連接。
    Post-linking function 全部元素都被連接後執行。

  compile function 和 link function須要注意的地方:二者是互斥的,compile function負責對模板dom進行修改,link function負責將做用域和dom進行鏈接。

                          當二者共存時,compile function返回的函數看成link function,link function則被忽略。

相關文章
相關標籤/搜索