angularjs培训之directive(1)

原文链接:http://www.sitepoint.com/practical-guide-angularjs-directives/

   在angular程序中directive是很重要的组件。尽管Angularjs内置了很多的指令,但是你也经常需要创建特定功能的指令。这篇文章给大家一个指令的基本概述并且详解怎么使用。


overview

   directive引进了新的语法声明。directive是通过绑定在DOM元素上的提供特定的功能,例如,静态的html不知道怎么创建和显示一个datepicker组件,为完成这个新语法功能的实现我们需要定义个directive。directive以某种方式窗机一个元素并且使他的行为类似一个datepicker。随后我们将看到这是如何实现的。

   如果你以前开发过angularjs应用程序,不管你意识到与否你应该使用过指令。你可能使用过简单的指令例如ng-model,ng-repeat,ng-show等等。所有的指令都是给元素上绑定特定的行为。例如,ng-repeat复制特定的元素,ng-show 条件成立则显示某些元素。如果你要实现一个元素的拖拽功能那么你统一需要一个指令directive背后的基本思想非常简单,它使HTML真正互动通过将事件监听器或DOM元素转换。

jquery透视

 考虑下如何使用jquery创建一个datepicker。首先添加一个input域然后调用jquery的$(element).datePicker()。事实上把输入的值转换成datepicker。当一个开发者检查标签时,ta可以立马就能猜出来这个输入框是什么元素?仅仅只是一个input域或是一个datepicker?你得去看下jquery的源码去确认下啦。angular的方法是使用directive扩展html。因此,datepicker的指令就可以像如下:

<date-picker></date-picker> 
或者 <input type="text" date-picker/>

这种方法创建的UI组件不但直观而且清晰。你看到这个元素就会知道它应该是做什么的。


构造自定义指令

 angularjs的指令有四种表现形式:

     1、新的html元素<date-picker></date-picker> 

     2、元素的属性<input type="text" date-picker/>

     3、class类<input type="text" class="date-picker"/>

     4、注释<!--directive:date-picker-->

   当然了,我们可以控制让directive怎么出现在html中。现在,让我们看下是怎么在angularjs中定义一个典型的指令。directive像注册controller一样,但是却返回一个包含多个属性的对象来配置directive,下面的代码展示了一个helloworld指令,

 var app = angular.module('myapp', []);
 
 app.directive('helloWorld', function() {
	return {
		restrict: 'AE',
		replace: 'true',
		template: '<h3>Hello World!!</h3>'
		};
	});

    在上面的代码,使用app.directive()在module里注册一个新指令,第一个参数是指令名字,第二个参数是一个function,并返回一个对象。如果directive有额外的对象或者service依赖,比如说$rootScope,$http,或者$compile,他们可以被注入。指令可以在HTML这样使用:<hello-world/> 或者 <hello:world/>,或者作为属性使用如下:<div hello-world></div>,<div hello:world/>。如果要遵从html5的特性,可以使用x-或者data-给属性加上前缀。所以,下面的标签会匹配到helloworld指令<div data-hello-world></div>或者<div x-hello-world></div>


 备注:当匹配指令时,angular去掉x-或者data-x的前缀,然后转换-或者:分隔字符串为骆驼命名法并且去匹配注册的指令,这就是因为我们需要在指令使用helloWorld在html中使用hello-world. 然而,上面的指令不仅仅显示静态文本,我们还有几个有趣的点需要解释。指令定义时我们定义了三个属性,让我们看下每个属性扮演的角色。

    restrict:html中怎么使用指令。在上面的例子中,我们设置的是'A(Attribute)E(Element)',因此指令可以在新html元素中或是属性。如果在class类中使用可以新增一个'C'。

    template : 当指令compile和linked时,特定的html片段会被应用。这里没有要求必须是简单的字符串,template可以是复杂的可以包含其他的指令,表达式等等。在大多数的场景中我们需要templateUrl代替template。因此,理想情况下我们应该定义html模板到单独html文件,在templateUrl指向html模板。

    replace:该配置项使template替换directive依附的html片段。在外面的例子中我们只用<hello-world></hello-world>并且replace 为true。因此,directive编译后,template选项中的内容会替换<hello-world></hello-world>,最终输出<h3>Hello World!!</h3>如果设置成false(默认值)当指令执行时template中会被插入到元素中。


link方法和Scope 

   directive的编译如果没有对应的scope那么指令是没有什么意义的。directive默认不会声明一个新的子scope,当然,新directive可以访问父的scope。这就意味着在controller中定义了一个directive将会使用这个controller的scope。  

   为了使用scope,我们可以使用一个叫做link的方法,可以在定义directive时返回该link。现在让我们来修改下我们的helloworld指令当我们输入一个颜色值的时候,'helloWorld'字的背景色就会自动改变,当我们点击文字的时候颜色恢复成白色  ,代码如下:

<body ng-controller="MainCtrl">
  <input type="text" ng-model="color" placeholder="Enter a color" />
  <hello-world/>
</body>

修改后的指令如下:

app.directive('helloWorld', function() {
  return {
    restrict: 'AE',
    replace: true,
    template: '<p style="background-color:{{color}}">Hello World',
    link: function(scope, elem, attrs) {
      elem.bind('click', function() {
        elem.css('background-color', 'white');
        scope.$apply(function() {
          scope.color = "white";
        });
      });
      elem.bind('mouseover', function() {
        elem.css('cursor', 'pointer');
      });
    }
  };
});

我们注意到,link方法接受三个参数:

 scope:传入directive的scope,在该例子中等同于其父级controller的scope

 elem:jqLite(jquery的子集)包装后的元素。如果引用了jquery,将会变成jquery的包装类。因为该元素已经用jquery/jqlite,所以无需再使用$()包装来操作DOM。

 attrs: 代表element元素声明的属性,例如,你可以在html中这么声明<hello-world some-attribute></hello-world>并且在link方法中可以使用attrs.someAttribute进行访问。


compile方法

   在link方法执行前,我们使用compile方法实现DOM转换 ,接受两个参数:

    tElement : 指令作用的元素

    attrs : 元素中声明的属性

注意:compile方法不能访问scope属性,但是必须返回一个链接函数。但是,如果没有compile方法你可以配置link方法,compile方法如下:

app.directive('test', function() {
  return {
    compile: function(tElem,attrs) {
      //do optional DOM transformation here
      return function(scope,elem,attrs) {
        //linking function here
      };
    }
  };
});

    大部分的时间,我们只需要link方法就可以正常工作了。这是因为很多指令用来注册事件,注册监听,更新DOM等等,这些都是定义在link方法中的ng-repeat指令,会clone和赋值多次DOM元素,在link方法执行前使用compile方法。问题来了,为什么要使两个方法分离?为什么我们只需要一个方法?

要回答这些问题我们必须了解angularjs是怎么编译的指令的?

指令怎么编译

    当应用程序启动时,angular开始使用$compile解析DOM,这项服务搜索的标记指令和对注册指令进行匹配,一旦所有的指令别识别,angular执行他们的compile方法,向前面提到的,compile返回一个链接函数,这个链接方法会被添加到连接方法的list中,list方法中链接方法稍后会运行,这个叫做编译阶段。如果指令需要实现clone多次(ng-repeat),我们可以让compile方法执行一次来获得性能的提升,但是link方法每次clone都要执行一次,这就是为什么compile方法不会接受scope参数了。

   经过编译阶段就是链接阶段,link方法串行执行,这是由指令生成的模板进行评估对正确的Scope,都变成实时的DOM的事件作出反应。

  改变指令的scope

   默认情况下directive获得父的scope。但是这种情况我们不想使用所有的场景。如果我们暴露父controller的scope到directive中,就能随意的改变scope中的属性。有些场景中,directive有可能给scope中增加属性和方法为了内部使用。如果我们在父scope中执行某些操作,则被污染了,

   所以,我们可以有两个选择:

   (1)子scope:这个scope原型继承它的父scope

   (2)独立scope:不继承父scope创建一个新的scope

   在指令定义是指定scope属性,代码片段如下:

 app.directive('helloWorld', function() {
  return {
    scope: true,  // use a child scope that inherits from parent
    restrict: 'AE',
    replace: 'true',
    template: '<h3>Hello World!!</h3>'
  };
});

   上面的代码要求angular创建新的从父作用域原型继承来的子作用域。还一个选择就是独立的scope,代码如下:

app.directive('helloWorld', function() {
  return {
    scope: {},  // use a new isolated scope
    restrict: 'AE',
    replace: 'true',
    template: '<h3>Hello World!!</h3>'
  };
});


你可能感兴趣的:(AngularJS,Directive)