FED实验室 - 专注WEB端开发和用户体验

NGJS-5 指令(Directives)-1

AngularJS教程 煦涵 2730℃ 0评论

Directives(指令)是AngularJS应用最重要的一部分。尽管AngularJS已经提供了非常丰富的指令,但根据项目需要,我们仍需要创建自定义指令。Directives系列教程讲述如何自定义指令及其在项目中实践,由于指令的内容较多我会分为几个部分来讲,在本系列的最后我会用指令来创建一个可复用的弹窗组件。

一个指令可用来引入新的HTML语法。指令可以用来扩展HTML,使用特定元素具有特定行为。比如静态的HTML不知道如何来创建和展示一个dialog组件。为了让HTML能识别这个语法,我们可以使用指令来实现。后面我们会介绍如何实现的。

从我们写第一个AngularJS应用的时候,我们就用到了指令ng-app。指令赋予了DOM元素特定的行为,ng-show显示一个元素,ng-repeat重复特定的元素等等。指令通过对元素的事件绑定、DOM操作使其具有真实的交互性。

一、新建一个自定义指令helloworld

指令的注册方式与controller相同,但是它返回一个指令配置属性对象,我们来看看下面代码:

<div ng-app="myApp">
	<helloworld></helloworld>
</div>

<script type="text/javascript">
	angular.module('myApp', [])
	.directive('helloworld', function() {
		return {
			restrict: 'AE',
			template: '<div>Hello World!</div>'
		};
	});
</script>

从Chrome控制台可以看到展示的HTML代码:

<helloworld>
	<div>Hello World!</div>
</helloworld>

二、指令默认配置对象及各属性默认值

从源码中可以看到,指令的默认自定义对象及配置属性默认值[angularjs-1.3.4/angular.js line 5703-6354]:

myModule.directive('directiveName', function factory(injectables) {
 var directiveDefinitionObject = {
   priority: 0,
   template: '<div></div>', // or // function(tElement, tAttrs) { ... },
   // or
   // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
   transclude: false,
   restrict: 'A',
   templateNamespace: 'html',
   scope: false,
   controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
   controllerAs: 'stringAlias',
   require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
   compile: function compile(tElement, tAttrs, transclude) {
     return {
       pre: function preLink(scope, iElement, iAttrs, controller) { ... },
       post: function postLink(scope, iElement, iAttrs, controller) { ... }
     }
     // or
     // return function postLink( ... ) { ... }
   },
   // or
   // link: {
   //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
   //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
   // }
   // or
   // link: function postLink( ... ) { ... }
 };
 return directiveDefinitionObject;
});

三、配置属性priority

当在一个DOM元素上定义多个指令时,可能需要用到priority属性来指定来指定这些指令被应用的顺序。
priority属性用在compile函数被调用之前对多个指令进行排序。priority被定义为一个数字,指令的priority值越大,越优先编译。priority默认值为0。看示例:

<div ng-app="myApp">
	<div ng-my-a ng-my-b></div>
</div>
<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			template: function(tElem, tAttr) {
				console.log("ng-my-a指令");
				return '<div>ng-my-b指令</div>';			
			}
		};
	})
	.directive('ngMyB', function() {
		return {
			priority: 1,
			restrict: 'A',
			template: function() {
				console.log("ng-my-b");
				return '<div>ng-my-b指令</div>';
			}
		};
	})
</script>

此时会报如下错错误:
Error: [$compile:multidir] Multiple directives [ngMyB, ngMyA] asking for template on:

问题大致描述是多个指令同时请求模板产生的问题。
在stackoverflow搜索了下,此类问题还是比较多的,修改上例如下:

<div ng-app="myApp">
	<div ng-my-a ng-my-b></div>
</div>
<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			compile: function() {
				console.log("ng-my-a-compile");
				return function() {
					console.log("ng-my-a-link");
				}
			}
		};
	})
	.directive('ngMyB', function() {
		return {
			priority: 1,
			restrict: 'A',
			template: '<div>ng-my-b指令</div>',
			compile: function() {
				console.log("ng-my-b-compile");

				return function() {
					console.log("ng-my-b-link");
				};
			}
		};
	})
</script>

Chrome控制台打印结果:

ng-my-b-compile 
ng-my-a-compile 
ng-my-a-link 
ng-my-b-link 

由此可以看到ngMyB指令先编译。具体实例请戳验证priority与compile的关系

四、配置属性restrict

Angular指令有以下四种表现形式:

`E` - Element name (default): `<my-directive></my-directive>`
`A` - Attribute (default): `<div my-directive="exp"></div>`
`C` - Class: `<div class="my-directive: exp;"></div>`
`M` - Comment: `<!-- directive: my-directive exp -->`

根据不同需求,合理使用。

五、配置属性replace


`true` - the template will replace the directive's element.
`false` - the template will replace the contents of the directive's element.

默认值为false,会替换掉指令元素的内容;如果为true,会替换掉指令元素;

<div ng-app="myApp">
	<div ng-my-a></div>
</div>
<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			template: '<div>ng-my-b指令</div>'
		};
	})
</script>
Chrome控制台HTML结构:
<div ng-my-a=""><div>ng-my-b指令</div></div>


//设置replace: true
<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			replace : true,
			template: '<div>ng-my-b指令</div>'
		};
	})
</script>
Chrome控制台HTML结构:
<div ng-my-a="">ng-my-b指令</div>

六、配置属性transclude

Transclusion(嵌入)

transclude:提取指令出现地方的元素的内容,使它对指令生效。这些内容被编译并提供给指令作为一个"transclude function"。有两种嵌入方式:嵌入指令元素的内容和整个指令元素。


`false` - default
`true` - transclude the content (i.e. the child nodes) of the directive's element.
`'element'` - transclude the whole of the directive's element including any directives on this element that defined at a lower priority than this directive. When used, the `template`
property is ignored.

当为true时,嵌入指令元素的内容。
当为element时,嵌入整个指令元素(包含比该指令元素的priority更小的指令)。设置element时,template属性被忽略。下面看看实例:

实例一:transclude: true

<div ng-app="myApp">
	<div ng-my-a>我是指令里的内容</div>
</div>
<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			transclude: true,
			template: '<div ng-transclude>ng-my-b指令</div>'
		};
	})
</script>
Chrome控制台查看Elements:
<div ng-app="myApp" class="ng-scope">
	<div ng-my-a="">
		<div ng-transclude="">
			<span class="ng-scope">我是指令里的内容</span>
		</div>
	</div>	
</div>

当家注意到此结构中多了个span元素,并添加了class="ng-scope",此时是创建了一个新的scope,它的原型继承父scope,在后面讲scope的时候我们还会提到。其中ng-tranclude决定了在什么地方放置嵌入部分。

实例二:transclude 与 replace共用

修改上例:

<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			transclude: true,
			replace: true,
			template: '<div ng-transclude>ng-my-b指令</div>'
		};
	})
</script>
Chrome控制台查看Elements:
<div ng-app="myApp" class="ng-scope">
	<div ng-transclude="" ng-my-a="">
		<span class="ng-scope">我是指令里的内容</span>
	</div>
</div>

看到区别么有~~~

实例三: element

<div ng-app="myApp">
	<div ng-my-a>我是指令里的内容</div>
</div>
<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			transclude: 'element',
			template: '<div ng-transclude>ng-my-b指令</div>'
		};
	})
</script>

Chrome控制台查看Elements:
<div ng-app="myApp" class="ng-scope">
	<!-- ngMyA:  -->
</div>

看到没有,在使用 transclude:'element'的时候,指令所在的元素会被转换成HTML注释。也就是说:

1.transclude:'element'和replace:false组合,指令模板本质上是被添加到了注释的innerHTML中——也就是说其实什么都没有发生!

2.transclude:'element'和 transclude:true组合,它将标记了ng-transclude指令的元素一起包含到了指令模板中。

<script type="text/javascript">
	angular.module('myApp', [])
	.directive('ngMyA', function() {
		return {
			restrict: 'A',
			transclude: 'element',
			replace: true,
			template: '<div ng-transclude>ng-my-b指令</div>'
		};
	})
</script>
Chrome控制台查看Elements:
<div ng-app="myApp" class="ng-scope">
	<div ng-transclude="" ng-my-a="">
		<div ng-my-a="" class="ng-scope">我是指令里的内容</div>
	</div>
</div>

transclude和template、replace、scope等都有联系,注意看几种在Chrome控制台的Elements对比,需要深入消化。以上就是本节内容,下一节我们将继续探讨Directives的其他配置属性。如果你对文中所述有疑问,欢迎给我留言。

更多阅读:
Develop ->Developer Guide -> HTML Compiler
Develop ->Developer Guide -> Directives
transclude:'element'

下面是「FED实验室」的微信公众号二维码,欢迎扫描关注:

FED实验室

行文不易,如有帮助,欢迎打赏!

赞赏支持 喜欢 (3)
捐赠共勉
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址