首 页  -  技术分享  - angularjs 指令(directive)详解(1)

angularjs 指令(directive)详解(1)

分享者:陈辉     2016-01-28

什么是directive?我们先来看一下官方的解释:

At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS'sHTML compiler ($compile) to attach a specified behavior to that DOM element (e.g. via event listeners), or even to transform the DOM element and its children.

Angular comes with a set of these directives built-in, like ngBindngModel, and ngClass. Much like you create controllers and services, you can create your own directives for Angular to use. When Angular bootstraps your application, the HTML compiler traverses the DOM matching directives against the DOM elements.

像ngBind,ngModel和ngClass等指令都是angular内置的directive,当我们感觉这些directive不能满足我们的要求时,则可以自己动手创建directive。

我们定义的directive一般使用camelCase(驼峰)命名规则,但是由于HTML是不区分大小写的,所以我们的directive在DOM中只能使用小写,可以使用以下几种方式来表示:

例如ngBind可以表示为以下几种方式:

  <span ng-bind="name"></span> <br/>
  <span ng:bind="name"></span> <br/>
  <span ng_bind="name"></span> <br/>
  <span data-ng-bind="name"></span> <br/>
  <span x-ng-bind="name"></span> <br/>

但我们一般常用下划线来表示,如:ng-bing

 

了解完指令的定义,那么接下来我们学习如何自己创建一个指令。

一.directive的写法以及参数说明:

angular.module("app",[])
 .directive("directive",function(){
    return{
     	restrict:"EACM", //指明指令在DOM中以什么样的形式被声明
		priority:0, //该指令的执行优先级
		terminal:true/false, //是否是最后一组执行的directive。
		template:"<div></div>", //模板
		templateUrl:"**/**.html", //指定模板的url  //与template只能存在其中一个
		templateNamespace: 'html'/'svg'/'math', //表示模板中标记使用的文档类型
		replace:true/false,  //替换或拼接到当前元素
		transclude:true/false/'element', //将内容编译后放入指定地方
		scope:true/false/{}, //创建一个新的作用域
		require:[],  //请求其他directive的controller
		controller:function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, //创建一个控制器,可与其他directive共享
		controllerAs: 'controllerName', //为controller创建一个别名
   		bindToController: false,
		compile: function compile(tElement, tAttrs, transclude) {
      		return {
        			pre: function preLink(scope, iElement, iAttrs, controller) { ... },
       			 post: function postLink(scope, iElement, iAttrs, controller) { ... }
      		} 
		},//通过表示服修改DOM模板
     	link: {
      		pre: function preLink(scope, iElement, iAttrs, controller) { ... },
     		 post: function postLink(scope, iElement, iAttrs, controller) { ... }
     	}// 或者  link: function postLink( ... ) { ... }
         //	与compile只能存在其中一个
    };
})

二.directive返回参数详解

 

1.restrict

可选参数,指明指令在DOM里面以什么形式被声明,默认为A(属性);

E(元素):<directive></directive>

A(属性):<div directive='name'></div>

C(类):   <div class='directive'></div>

M(注释):<–directive:directive name–>

 

2.priority

可选参数,指明指令的优先级,若在单个DOM上有多个指令,则优先级高的先执行,如果优先级相同,则执行顺序是不确定的。

 

3.terminal

可选参数,可以被设置为true或false,若设置为true,则表示当前的priority将会成为最后一组执行的directive。任何directive与当前的优先级相同的话,他们依然会执行,但顺序是不确定。优先级低于此指令的其他指令则无效,不会被调用。

 

4.template

可选参数,html代码,如下所示,

//js代码
angular.module("app",[])
 .directive("hello",function(){
	return{
		restrict:'EA',
		template:"<div>templateUrl</div>"
	};
 })
//html代码
<div>
	<hello></hello>
</div>

则输出结果是:

templateUrl

 

5.templateUrl

可选参数,与template基本一致,但模版通过指定的url进行加载。因为模版加载是异步的,所以compilation、linking都会暂停,等待加载完毕后再执行。

 

由于加载html模板是通过异步加载的,若加载大量的模板会拖慢网站的速度,可以先缓存模板来提高速度(可以使用ng-template或$templateCache来缓存模板,详细用法在这里不多说,请自行查询)

 

 6.templateNamespace

可选参数,默认值为'html'。

'html' -模板中所有的根节点都是html。根节点也可能是最高级别的的元素,例如<svg>或者<math>

'svg' - 根节点是svg元素

'math' - 根节点是math元素

 

 7.replace

可选参数,默认为false。如果设置为true,那么模版将会替换当前元素,否则作为子元素添加到当前元素中。

当设为true时:

//js代码
angular.module("app",[])
 .directive("hello",function(){
	return{
		restrict:'EA',
		replace:true,
		template:"<div>templateUrl</div>"
	};
 })
//html代码
<div>
	<hello></hello>
</div>

 

那么渲染之后的代码为:

 <div>
	<div>templateUrl</div>
</div>

 

可以看到,<hello></hello>已经被<div>templateUrl</div>这个标签替换掉了。

还是以上代码,如果设为false或不设值时,渲染之后的代码为:

<div>
	<hello>
		<div>templateUrl</div>
	</hello>
</div> 

 

可以自己比较一下true和false的区别。

 

8.transclude

可选参数,默认值为fasle。

指令的作用是把我们自定义的语义化标签替换成浏览器能够认识的HTML标签。那么,如果我们自定义的标签内部出现了子标签,应该如何去处理呢?

transclude可以让我们提取包含在指令那个元素里面的内容,再将它放置在指令模板的特定位置。我们可以使用ng-transclude来指明了应该在什么地方放置transcluded内容。

看如下代码:

 <!doctype html>
<html ng-app="myApp">
<head>
  <script src="angular.min.js"></script>
</head>
<body>

  <div ng-controller='controller'>
  	<hello>
       {{text}}
    </hello>
	<hello></hello>
  </div>

  <script>
    var app = angular.module('myApp',[]);
    app.controller('controller',function($scope){
      $scope.text = 'I am transclude';
    });
    app.directive('hello',function(){
     return {
        scope:{},  
        restrict: 'AE',
        transclude: true,
        template: '<div ng-transclude>hello world</div>'
     }
    });
  </script>
</body>
</html>

上边代码的输出结果是:

 I am transclude

hello world

 

当transclude设为true的时候,会创建一个新的transclude空间,并且继承了父作用域

我们再看看生成的html为下图所示,

可以发现第一个<hello>标签里的文本“hello world”消失了,这是因为被transclude内容替换掉了。这里的transclude内容就是{{text}}

那么当我们使用transclude:'element'时,会发生什么情况呢?

查看结果,我们会发现transclude:'element'在实际使用中消失,被替换成了HTML注释。

那么原来的元素究竟发生了什么?我们可以看如下代码:

<!DOCTYPE html>
<html>
<head>
<script src="https://angular.min.js"></script>
  <meta charset="utf-8">
  <title>Element Transclusion Demo</title>
</head>
<body ng-app="myApp">
  <hello>
    This content will disappear and reappear
  </hello>
  <script>
   var app = angular.module('myApp', []);
app.directive('hello', function() {
  return {
    transclude: 'element',
    link: function(scope, element, attr, ctrl, transclude) {
      element.after(transclude());
    }
  };
});
  </script>
</body>
</html>

生成的html代码如下:

我们可以看出,将他放到transclusion function注释之后,内容就显示了出来。

我们再看如下代码:

<!DOCTYPE html>
<html>
<head>
<script src="angular.min.js"></script>
  <meta charset="utf-8">
  <title>Element Transclusion Demo</title>
</head>
<body ng-app="myApp">
  <div my-show-each="['one', 'two', 'three']">
    {{myEach}}
  </div>
  <script>
 var app = angular.module('myApp', []);
 app.directive('myShowEach', function() {
  return {
    scope:{
      myShowEach:'='
    },
    transclude: 'element',
    link: function(scope, element, attrs, ctrl, transclude) {
      
      scope.myShowEach.forEach(function(each) {
        transclude(function(transElement, transScope) {
          transScope.myEach = each;
          element.parent().append(transElement);
        });
      });
    }
  };
});
  </script>
</body>
</html>

输出结果为:

one
two
three
我们重复渲染了Element Transclusion,可以看作是一个简易的ng-repeat。