angularjs Directives的使用
更新日期:
这篇文章用来介绍angular里面directives的使用,大部分是参考的官方的教程。由于本人也是学习之中,难免有错误的地方。欢迎指正。
什么是directive?
官方是这么说的:directive是dom节点上的一种标签。angular的解析引擎匹配到这个标签后,会给当前的dom节点增加行为,或者改变dom节点和子节点。
说白了就是用来在当前节点上干点特别的事的。
系统自带的一些directive的介绍
其实之前我们已经用过很多directive了。下面对一些主要的做一些介绍。
ng-app
这个算是最常用的directive了,这个directive的作用就是启动当前应用,让angular从它指向的节点开始一层层的遍历。然后做一系列的编译操作。后面可以跟上模块名。
ng-model
这个标签的作用是,修改当前control上的$scope对象的属性值。这个标签经常使用在input,select, textarea 这些dom节点上。比如 ngmodel="v"
就会将当前的dom节点(input,select, textarea)的值跟$scope的v属性值绑定,这样你重新输入一个值后,$scope的v属性也会自动变化。
ng-controller
这个标签很简单,就是用来指定当前dom使用哪个control函数的。这样当前dom下可以直接访问到control函数的$scope所有属性还有方法。
ng-click,ng-blur,ng-mousedown等
这些标签都是用来监听事件的。后面可以跟相应的事件处理函数。
ng-show
这类标签其实之前有介绍过,主要用来处理dom的显示和隐藏的。ng-show=”v”的机制是这样的。当$scope.v的值为非true的值时就会给当前的dom节点增加一个class => ng-hide 并且这个class被angular设成了。.ng-hide {display: none !important;}
,也就是说只有$scope.v为true类型的值时,这个dom才会显示出来。
自己编写directive
自己编写directive可以实现很多功能,主要通过当前模块的directive方法实现。大概有如下这些类型:
下面一一介绍。
修改当前的dom节点
这类directive主要用来修改当前的dom的一些属性,或者显示隐藏这类的。
下面看个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <!doctype html> <html > <head> <meta charset="utf-8"> <script src="angular.js"></script> <script type="text/javascript"> var Dm = angular.module('testDirective',[]); Dm.controller('DmCtrl', function($scope) { $scope.money="123" }); Dm.directive('moneyWrap', function() { function link(scope, element, attrs) { element.text(scope[attrs.cuName]+"¥") } return { link: link }; }); </script> </head> <body ng-app="testDirective"> <div class="wrap" ng-controller="DmCtrl"> <h2>用来测试directive的例子</h2> <span money-wrap cu-name="money"></span> </div> </body> </html> |
非常简单的例子,用于打印当前的金钱,末尾加个¥。
首先directive方法用来在当前模块里生成一个标签。返回一个对象:
1 2 3 | return { link: link }; |
对象里面指定了link函数。这个link函数就是用来操作dom的主。
我们看下link函数的参数:
- scope
这个就是当前control的$scope对象,没啥好说的。 - element
这个是当前dom节点经过“jquery”处理过后的对象。用过jquery的都知道这个概念。当然这边的jquery是angular自己实现的阉割版的jquery。方法少了许多,不过按angular的话说,够用了。 - attrs
这个对象包含,当前的dom节点上的各种属性标签的值。不过要注意的是这边会将xxx-bbb这种形式的改写成xxxBbb的这种驼峰形式。
看看我们的link函数吧:
1 2 3 | function link(scope, element, attrs) { element.text(scope[attrs.cuName]+"¥") } |
简简单单的一句话就涵盖了三个参数哈。
意思很简单,就是先通过attrs.cuName获取到属性名称money,之后拿到model的值,再通过text(jquery的方法)操作dom的值。
这样一个简单的directive就实现了。
directive也可以简写,直接返回一个link函数。
这样上面的就可以简写为:
1 2 3 4 5 | Dm.directive('moneyWrap', function() { return function (scope, element, attrs) { element.text(scope[attrs.cuName]+"¥") } }); |
模板类的directive
前面提到的都是通过link函数来操作当前dom本身的。directive还支持一种模板类的扩展形式。
下面先看个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <!doctype html> <html > <head> <meta charset="utf-8"> <script src="angular.js"></script> <script type="text/javascript"> var Dm = angular.module('testDirective',[]); Dm.controller('DmCtrl', function($scope) { $scope.me = { money:"123" } }); Dm.directive('moneyWrap', function() { return { template: '你值: {{me.money}}¥' }; }); </script> </head> <body ng-app="testDirective"> <div class="wrap" ng-controller="DmCtrl"> <h2>用来测试directive的例子</h2> <span money-wrap></span> </div> </body> </html> |
通过template属性。可以设置模板。
不过这样只能设置字符串,显然不是最好的方式。我们可以用templateUrl: 'my.html'
来加载script模板,或者其他文件。
所以我们可以这么变:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <!doctype html> <html > <head> <meta charset="utf-8"> <script src="angular.js"></script> <script type="text/javascript"> var Dm = angular.module('testDirective',[]); Dm.controller('DmCtrl', function($scope) { $scope.me = { money:"123" } }); Dm.directive('moneyWrap', function() { return { templateUrl: 'my.html' }; }); </script> <!-- my.html --> <script type="text/ng-template" id="my.html"> 你值: {{me.money}}¥ </script> </head> <body ng-app="testDirective"> <div class="wrap" ng-controller="DmCtrl"> <h2>用来测试directive的例子</h2> <span money-wrap></span> </div> </body> </html> |
不过记得angular做了限制,这个必须在服务器里才能跑。不然angular会报跨域的错误。你可以用个nodejs,或者php开个服务测试。
我们能做的其实有很多。下面介绍些高级的用法。
我们知道directive会返回一个对象,这个对象其实支持很多配置值。下面介绍这些比较高端的类容:
1. restrict
restrict用来指定directive的形式,支持“A”,”E”,”AE”三种。A就是attribute,E是element。默认是A也就是我们的用法;<span money-wrap></span>
。如果我们改成“E”就要这么用:<money-wrap></money-wrap>
。“EA”就是两种都可以用,很简单吧。
2.scope
这个属性,有人一看直接就是一个子标签的scope对象吧。用来定义自己的scope属性。这话是不对的,这边的scope有个专门的名字,叫isolate scope
。如果你没有定义这个scope的话,那么你的子模板,也就是使用templateUrl引用的html里面使用的scope会是外面的control的。但是你一旦定义了scope,那么对不起,外面的那个scope就不能用了,那种原型链式的属性访问也不存在了。
下面看个官方的例子学习下:
html如下:
1 2 3 4 5 6 7 8 9 10 11 | <div ng-app="docsIsolationExample"> <div ng-controller="Ctrl"> <my-customer info="naomi"></my-customer> </div> <!-- my-customer-plus-vojta.html --> <script type="text/ng-template" id="my-customer-plus-vojta.html"> Name: {{customerInfo.name}} Address: {{customerInfo.address}} <hr> Name: {{vojta.name}} Address: {{vojta.address}} </script> </div> |
js如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | angular.module('docsIsolationExample', []) .controller('Ctrl', function($scope) { $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' }; $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' }; }) .directive('myCustomer', function() { return { restrict: 'E', scope: { customerInfo: '=info' }, templateUrl: 'my-customer-plus-vojta.html' }; }); |
还是很好理解的吧,只要注意这边这个
1 2 3 | scope: { customerInfo: '=info' }, |
这个意思是设置一个isolate scope
这个isolate scope
里面有个customerInfo属性,而这个属性的值等于info。info是哪里来的呢?你看下html里面的第三行;
1 | <my-customer info="naomi"></my-customer> |
看见没有,引用directive的时候,有个属性info设置为了外面control的scope里面的naomi。这样angular就会把外面的Ctrl的$scope.naomi对象赋值到isolate scope
的customerInfo属性上。注意这边是克隆了一份,所以你修改isolate scope
里面的值是不会影响到外面的。
isolate scope
里面的属性名如果与directive上面的属性一样,可以只简写为=。也就是说可以这么写scope:scope: {info: '='}
这边不止支持=,还支持@,&符号。@符号是用来赋值string类型的值的。而&符号可以用来执行表达式和方法。比如你定义
1 | <my-customer info="naomi" t="1111{{naomi.name}}" x="1+1"></my-customer> |
那你就得这么调用:
1 2 3 4 5 | scope: { customerInfo: '=info', t: '@', x:'&' }, |
这样t的值为‘1111Naomi’。x为‘2’。
3.transclude
transclude属性用来实现这样的事情,就是directive包含的类容会使用外面的scope对象,而不使用内部的。看下面这个例子:
html:
1 2 3 4 5 6 7 8 9 10 | <div ng-app="docsTransclusionDirective"> <div ng-controller="Ctrl"> <my-dialog>Check out the contents, {{name}}!</my-dialog> </div> <!-- my-dialog.html --> <script type="text/ng-template" id="my-dialog.html"> <div class="alert" ng-transclude> </div> </script> </div> |
js:
1 2 3 4 5 6 7 8 9 10 11 12 | angular.module('docsTransclusionDirective', []) .controller('Ctrl', function($scope) { $scope.name = 'Tobias'; }) .directive('myDialog', function() { return { restrict: 'E', scope:{}, transclude: true, templateUrl: 'my-dialog.html' }; }); |
这边结果为Check out the contents, Tobias!
由于定义了isolate scope
,所以不能访问外面的control的scope,很麻烦。
但是这边 使用了transclude: true。<my-dialog>Check out the contents, {{name}}!</my-dialog>
是可以使用外面的scope对象的。解析完成后。在my-dialog.html里面我们通过系统自带的ng-transclude标签将那边的内容直接全部引入过来。这个主要使用在dialog这样的场景里。
4.require与controller
这两个属性用于两个directive的之间的相互作用。
直接看下面的例子吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | angular.module('docsTabsExample', []) .directive('myTabs', function() { return { restrict: 'E', scope: {}, controller: function($scope) { this.addPane = function(pane) { }; } }; }) .directive('myPane', function() { return { require: '^myTabs', restrict: 'E', scope: {}, link: function(scope, element, attrs, tabsCtrl) { tabsCtrl.addPane(scope); } }; }); |
使用并不复杂。
上面的myTabs定义了controller,这个其实就相当于在当前的directive节点上增加了 ng-controller标签一样。就是定义了一个control处理函数。
下面的myPane通过require: ‘^myTabs’可以向上查找。^的意思是向父标签寻找。这边是找寻myTabs。找到myTabs后,当前directive的link函数就可以拿到require进来的directive里面定义的controller。这边就是tabsCtrl。通过它就可以调用myTabs里面的一系列方法。
总结
可以看到directives非常灵活,可以处理很多事情,但是不能烂用。实际上这边的模板各种引用,后面会介绍使用路由来实现这种模板的各种相互关系。这是后面的内容了。