AngularJS中的指令

说到AngularJS,咱们首先想到的大概也就是双向数据绑定和指令系统了,这二者也是AngularJS中最为吸引人的地方。双向数据绑定呢,感受没什么好说的,那么今天咱们就来简单的讨论下AngularJS这个框架的指令系统,本人也是初学,查阅了一些资料,要是有一些说的很差的地方,万望指出。css

指令做为AngularJS中最为重要的部分,因此这个框架自己也是自带了比较多的的指令,可是在开发中,这些指令一般不能知足咱们的须要,因此咱们也是须要自定义一些指令的。那么一个AngularJS指令在HTML代码中能够有四种表现形式:数组

一、做为一个新的HTML元素来使用。app

<hello></hello>或者<hello/>

二、做为一个元素的属性来使用框架

<div hello></div>

三、做为一个元素的类来使用函数

<div class="hello"></div>

四、做为注释来使用性能

<!--directive: hello -->

注意这里有一个陷阱,就是在“directive: hello”这个的后面要有一个空格,否则的话是没有效果的,同时推荐注释的方法的仍是少用,若是非要追求高大上,那就随性吧。既然指令有以上四种表现形式,那么具体他是怎么来定义的呢?学习

.directive('hello',function(){
      return {
        restrict:'AECM',
        template:'<button>click me</button>'
      }
    })

上面就是定义一个指令最简单的代码了,没有之一。在上面的代码中,directive()这个方法定义了一个新的指令,该方法有两个参数,第一个'hello'就是规定指令的名字为hello,第二个参数是返回指令对象的函数。那么在上面的代码中,该函数主要使用了两个属性来定义这个hello指令:ui

一、restrict[string]这个属性,主要是用来规定指令在HTML代码中可使用什么表现形式。A表明属性、E表明元素、C表明类、M表明注释。实际状况中咱们通常使用AE这两种方式。this

二、template[string or function]这个属性,规定了指令被Angular编译和连接(link)后生成的HTML标记,这个属性能够简单到只有一个HTML文本在里面,也能够特别复杂,当该属性的值为function的时候,那么该方法返回的就是表明模板的字符串,同时也能够在里面使用{{}}这个表达式。spa

template: function () {
          return '<button>click me</button>';
 }

可是在通常状况下,template这个属性都会被templateUrl取代掉,用它来指向一个外部的文件地址,因此咱们一般把模板放在外部的一个HTML文件中,而后使用templateUrl来指向他。

在定义指令的时候,除了以上两个最基础的属性外,咱们还会使用到其余的不少属性,那么下面咱们就来一一的说下:

一、priority[number]属性,这个属性是来规定自定义的指令的优先级的,当一个DOM元素上面有一个以上的指令的时候,就须要去比较指令的优先级了,优先级高的指令先执行。这个优先级就是用来在执行指令的compile函数前,先排序的,那么关于compile这个函数,咱们会在下面仔细的说下。

二、terminal[boolean]属性,该参数用来定义是否中止当前元素上比本指令优先级低的指令,若是值为true,就是正常状况,按照优先级高低的顺序来执行,若是设置为false,就不会执行当前元素上比本指令优先级低的指令。

三、replace[boolean]属性,这个属性用来规定生成的HTML内容是否会替换掉定义此指令的HTML元素。当咱们把该属性的值设为true的时候,打开控制台看看你会发现这个指令生成的元素会是这样的:

当咱们设置为false的时候会是这样的:

四、link[function]属性,在上面的例子中,咱们自定义的指令其实没有多大意义,这只是一个最简单的指令,有好多的属性咱们都没有为他定义,因此没有多大用途。好比这个link函数,它包括三个参数:scope、element、attrs。这个link函数主要是用来添加对DOM元素的事件监听、监视模型属性变化、以及更新DOM的。它里面三个参数:

一:scope参数,在咱们没有为指令定义scope属性的时候,那么他表明的就是父controller的scope。

二:element参数,就是指令的jQLite(jQuery的子集)包装DOM元素。若是你在引入AngularJS以前引入了jQuery,那么这个元素就是jQuery元素,而不是jQLite元素。因为这个元素已经被jQuery/jQLite包装了,因此咱们就在进行DOM操做的时候就不须要再使用 $()来进行包装。

三:attrs参数,它包含了该指令所在元素的属性的标准化参数对象。

五、scope[boolean or object]属性,该属性是用来定义指令的scope的范围,默认状况下是false,也就是说该指令继承了父controller的scope,能够随意的使用父controller的scope里的属性,可是这样的话就会污染到父scope里的属性,这样是不可取的。因此咱们可让scope取如下两个值:true和{}。

当为true的时候,表示让Angular给指令建立一个继承于父scope的scope。

var myapp=angular.module('myapp',[])
    .controller('myctrl',['$scope', function ($scope) {
      $scope.color='red';
    }])
    .directive('hello', function () {
      return{
        restrict:'AECM',
        replace:true,
        template:'<button ng-click="sayhello()" style="background-color: {{color}}">click me</button>',
        scope:true,
        link: function (scope,elements,attrs) {
          elements.bind('click', function () {
            elements.css('background-color','blue');
          })
        }
      }
    })

这里咱们为父scope定义了一个color的属性,并赋值为red,在hello指令的scope属性中,咱们给了true,因此angular就为这个指令建立了一个继承于父scope的scope,而后在template属性中,咱们用{{}}使用了从父scope中继承过来的color属性,因此按钮会是红色的。

当为{}的时候,表示建立一个隔离的scope,不会继承父scope的属性。可是在有的时候咱们也要须要访问父scope里的属性或者方法,那么咱们应该怎么办呢。angular早就为咱们想到了这一点,有如下的三个办法可让咱们记性上面的操做:

一:使用@实现单向绑定,若是咱们只给scope的这个{}值的话,那么上面代码的按钮的背景色将会是灰色的。,而若是咱们须要使用父scope的color属性的时候,咱们能够这样写:

scope{
    color:'@color'
}
<hello color="{{color}}"></hello>

这里有两点须要注意:一、scope里的属性color表明的是模板{{}}这个表达式里面的color,二者必须一致。二、scope里的属性color的值,也就是@后面的color,表示的是下面的HTML元素里的属性color,因此这二者也必须一致,当这里的属性名和模板里表达式{{}}里面使用的名称相同的话,就能够省略掉@后面的属性名了,而后写成下面的形式。

scope{
    color:'@'
}

从指令中scope的值能够看出,指令模板中的表达式{{}}里的color的指向的是当前元素元素的color属性,这个color属性的值就是父scope的属性color的值。父scope把他的color属性值传递给了当前元素的color属性,而后color属性又把值传递给了模板中表达式里的color,这个过程是单向的。

二:使用=实现双向绑定

    .directive('hello', function () {
      return{
        restrict:'AECM',
        replace:true,
        template:'<button style="background-color: {{color}}">click me</button>',
        scope:{
          color:'='
        },
        link: function (scope,elements,attrs) {
          elements.bind('click', function () {
            elements.css('background-color','blue');
            scope.$apply(function () {
              scope.color='pink';
            })
          })
        }
      }
    })
<hello color="color"></hello>
<input type="text" ng-model="color"/>

这里咱们给指令的scope中的color属性和父scope中的color属性进行了双向绑定,而且给指令的link函数里,添加了一个单击事件,点击按钮会让按钮的颜色发生变化,而且改变指令scope的color属性的值,再给HTML页面中加了一个input标签,输出或者输入父scope的color属性的值。这里有一个地方须要注意:当前元素的属性名的值不用再加上{{}}这个表达式了,由于这里父scope传递的是一个真实的scope数据模型,而不是简单的字符串,因此这样咱们就能够传递简单的字符串、数组、甚至复杂的对象给指令的scope。如今让咱们来看看点击这个按钮将会发生什么。

这里咱们能看到,按钮的颜色变成了粉色的,说明点击让指令的scope的color属性发生了变化,从而致使按钮的颜色发生了变化。可是这里不只仅是按钮发生了变化,注意看,input表单里的值也变成了pink,这就说明父scope的color属性也发生了变化。 另外,再让咱们来给input里面输入一个颜色,看看发生什么变化。

,能够看出当咱们在表单里输入另一种颜色的时候,按钮的颜色也发生了变化,这就说明指令的scope的color属性被改变了。综上咱们能够发现使用'='实现的是双向绑定。

三:使用&调用父scope里的方法

var myapp=angular.module('myapp',[])
    .controller('myctrl',['$scope', function ($scope) {
      $scope.color='red';
      $scope.sayhello= function () {
        alert('hello');
      };
    }])
    .directive('hello', function () {
      return{
        restrict:'AECM',
        replace:true,
        template:'<button ng-click="sayhello()" style="background-color: {{color}}">click me</button>',
        scope:{
          color:'=',
          sayhello:'&'
        },
        link: function (scope,elements,attrs) {
          elements.bind('click', function () {
            elements.css('background-color','blue');
            scope.$apply(function () {
              scope.color='pink';
            })
          })
        }
      }
    })
<hello color="color" sayhello="sayhello()"></hello>
<input type="text" ng-model="color"/>

这里咱们也有两个地方须要注意:一、咱们不只须要在模板中使用ng-click指令,绑定上要调用的父scope中的方法,并且要在给当前元素添加一个属性,而且这个属性指向要调用的父scope的方法。二、指令scope的属性sayhello、当前元素的属性sayhello、模板绑定的事件方法名sayhello这三者要一致。那么这样咱们就能够点击按钮,弹出一个对话框了。

六、transclude[boolean]属性,这个属性用来让咱们规定指令是否能够包含任意内容

.directive('hello', function () {
      return{
        restrict:'AECM',
        replace:true,
        transclude:true,
        scope:{},
        template:'<div ng-transclude></div>',
      }
    })
<hello>
  hello
  <span>{{color}}</span>
</hello>

当他的值为true的时候,这是页面上输出的值。当为false的时候,页面上将会是空白的。这里有一个地方须要注意,就是<span>{{color}}</span>,这里的color是父scope里的color。并非指令里的scope的color属性。

七、compile[function]参数,该方法有两个参数element,attrs,第一个参数element指指令所在的元素,第二个attrs指元素上赋予的参数的标准化列表。这里咱们也有个地方须要注意:compile 函数不能访问 scope,而且必须返回一个 link 函数。可是若是没有设置 compile 函数,你能够正常地配置 link 函数,(有了compile,就不能用link,link函数由compile返回)。

.directive('hello', function () {
      return{
        restrict:'AECM',
        replace:true,
        translude:true,
        template:'<button ng-click="sayhello()" style="background-color: {{color}}">click me</button>',
        scope:{
          color:'=',
          sayhello:'&'
        },
        compile: function (element,attrs) {
          return function (scope,elements,attrs) {
            elements.bind('click', function () {
              elements.css('background-color','blue');
              scope.$apply(function () {
                scope.color='pink';
              })
            })
          };
        }
      }
    })

如今让咱们来点击这个按钮

咱们发现,这里点击按钮以后发生的事情和前面用link属性的同样,这实际上是没有多少差异的。

其实在大多数的状况下,咱们只须要使用 link 函数。这是由于大部分的指令只须要考虑注册事件监听、监视模型、以及更新DOM等,这些均可以在 link 函数中完成。 可是对于像 ng-repeat 之类的指令,须要克隆和重复 DOM 元素屡次,在 link 函数执行以前由 compile 函数来完成。那么为何咱们须要两个分开的函数来完成生成过程,为何不能只使用一个?要回答好这个问题,咱们须要理解指令在Angular中是如何被编译的!

八、指令是如何被编译的

当咱们的angular应用引导启动的时候,angular将会使用$compile服务遍历DOM元素,在全部的指令都被识别以后,将会调用指令的compile方法,返回一个link函数,而后将这个link函数添加到稍后执行的 link 函数列表中,这个过程被称为编译阶段。像ng-repeat这样的指令,须要被重复克隆不少次,compile函数只在编译阶段被执行一次,而且复制这些模板,可是link 函数会针对每一个被复制的实例被执行。因此分开处理,让咱们在性能上有必定的提升(这句话有点不太理解,我是从别的地方copy过来的。原文在这里http://blog.jobbole.com/62249/)。

九、controller[string or function]和require[string or string[]]参数,当咱们想要容许其余的指令和你的指令发生交互时,咱们就须要使用 controller 函数。当另外一个指令想要交互时,它须要声明它对你的指令 controller 实例的引用(require)。

.directive('hello', function () {
      return{
        scope:{},
        require:'^he',
        compile: function (element,attrs) {
          return function (scope,elements,attrs,cntIns) {
            cntIns.fn()
          };
        }
      }
    })
    .directive('he', function () {
      return {
        restrict:'AE',
        scope:{},
        controller: function ($scope, $compile, $http) {
          this.fn= function () {
            alert('hello');
          };
        }
      }
    })
<he>
  <hello color="color" sayhello="sayhello()"></hello>
</he>

当页面加载完毕以后,会弹出一个对话框。

好了上面就是我这段时间学习angular,所了解到的指令的知识,就先写到这里了。