Angular自定义指令(进阶)
2016-03-16 10:38
549 查看
现在我们定义一个专门用于显示客户信息的指令,客户信息包含“姓名”,“电话号码”,指令定义如下:
在模板中,我们通过表达式绑定了数据。然后我们使用该指令:
显示的内容如下:
name: GAMELoft9
phoneNumber:123456
name: GAMELoft9
phoneNumber:123456
确实显示了用户信息,看似一切正常。但这样似乎不合理,因为每一次使用<my-customer>标签,显示的都是同一个用户信息,如果要显示不同的用户信息,那我们不得不指定不同的控制器,因为数据是在控制器中的$scope(包括父级$scope)中保存的。如果真那样做,那么我们的控制器将会膨胀得不可收拾。按照正常的思路,应该是动态的向标签传递数据,例如如<my-customer customer="customer1"></my-customer>,这样才能保证我们的指令是可以重用的。
我们希望指令有一个自己的$scope,并绑定到指令的属性,这样就可以像使用标签的属性一样使用指令的属性。因为指令自己的$scope与controller的$scope是隔离开来的(不隔离的话指令中的表达式取值还是会去controller中寻找),因此我们把指令自己的这个$scope叫做隔离作用域(Isolate Scope)。隔离作用域通过scope进行配置,如下所示:
然后就可以如下使用指令了:
结果如下:
name: GAMELoft9
phoneNumber:123456
name: vivian
phoneNumber:999999
可以看到,有了隔离作用域,自定义指令的可复用性就大大提高了!自定义指令有了隔离作用域,就不需要直接同外面controller打交道,而是通过指令的属性间接的通controller交互。这里是不是有一点面向接口编程的味道了?通过属性这个“接口”定义一些操作,然后通过控制器去实现这个操作。然后隔离作用域就可以使用,而不用关心是哪个控制器来实现的!如下图所示:
其中隔离作用域中的变量绑定到属性名称,属性中的值为字符串值、控制器中的变量或者函数。隔离作用域和属性的之间的绑定策略有如下三种:
@:绑定的属性值为字符串。此时没有与controller交互。
=:绑定的属性值为变量。此时与controller有交互。
&:绑定的属性值为函数。此时与controller有交互。
如果隔离作用域的变量名称和指令属性名称相同,则可以省略后面的属性名称。请看下面的例子:
结果如下:
no:01
name: GAMELoft9
phoneNumber:123456
点击save后,会弹出“GAMELoft9 has been saved!”
其中no是绑定的字符串值,customer绑定是变量,onsave绑定的是函数。
之前我们有使用ng-controller来使用控制器,其实也可以在指令内部指定控制器,而且这种方式更好,如下所示:
而且还可以自己指定一个供内部使用的控制器:
这是一个仅供内部使用的控制器,是一个匿名函数。这里在控制器的函数中使用了$scope等服务,其实可以不用,如下:
我们使用了controllerAs给控制器取了一个别名,这样在作用域中也可进行访问:
这种方式的好处是控制器中没有注入$scope,同$scope是隔离的,易于测试。
和控制器类似的还有link函数,它也可以进行DOM操作。
指令的控制器和link函数可以进行互换。控制器主要是用来提供可在指令间复用的行为,而链接函数只能在当前内部指令中定义行为,且无法在指令间复用。link函数可以将指令互相隔离开来,而controller则定义可复用的行为。由于指令可以require其他指令所使用的控制器,因此控制器常被用来放置在多个指令间共享的动作。
如果我们希望将当前指令的API暴露给其他指令使用,可以使用controller参数,否则可以使用link来构造当前指令元素的功能性。如果我们使用了scope.$watch()或者想要与DOM元素做实时的交互,使用link函数会是更好的选择。技术上讲,$scope会在DOM元素被实际渲染之前传入到控制器中。在某些情况下,例如使用了嵌入,控制器中的作用域所反映的作用域可能与我们所期望的不一样,这种情况下,$scope对象无法保证可以被正常更新。当想要同当前屏幕上的作用域交互时,可以使用被传入到link函数中的scope参数。
var myApp = angular.module('app', []); myApp.controller("customerCtl", function ($scope) { $scope.customer1={//定义了一个用户 name:"GAMELoft9", phoneNumber:"123456" }; }).directive("myCustomter",function(){ return {//在这里定义指令 restrict:"E", template:"<div>" + "name:<span> {{customer1.name}}</span><br>"//绑定了用户数据 +"phoneNumber:<span>{{customer1.phoneNumber}}</span>" +"</div>" }; });
在模板中,我们通过表达式绑定了数据。然后我们使用该指令:
<!doctype html> <html ng-app="app"> <head> <script src="js/angular.js"></script> <script src="js/default.js"></script> </head> <body> <my-customter ng-controller="customerCtl"></my-customter> <br> <my-customter ng-controller="customerCtl"></my-customter> </body> </html>
显示的内容如下:
name: GAMELoft9
phoneNumber:123456
name: GAMELoft9
phoneNumber:123456
确实显示了用户信息,看似一切正常。但这样似乎不合理,因为每一次使用<my-customer>标签,显示的都是同一个用户信息,如果要显示不同的用户信息,那我们不得不指定不同的控制器,因为数据是在控制器中的$scope(包括父级$scope)中保存的。如果真那样做,那么我们的控制器将会膨胀得不可收拾。按照正常的思路,应该是动态的向标签传递数据,例如如<my-customer customer="customer1"></my-customer>,这样才能保证我们的指令是可以重用的。
我们希望指令有一个自己的$scope,并绑定到指令的属性,这样就可以像使用标签的属性一样使用指令的属性。因为指令自己的$scope与controller的$scope是隔离开来的(不隔离的话指令中的表达式取值还是会去controller中寻找),因此我们把指令自己的这个$scope叫做隔离作用域(Isolate Scope)。隔离作用域通过scope进行配置,如下所示:
//default.jsvar myApp = angular.module('app', []); myApp.controller("parentController", function ($scope) { $scope.customer1={//用户信息 name:"GAMELoft9", phoneNumber:"123456" }; $scope.customer2={//用户信息 name:"vivian", phoneNumber:"999999" }; }).directive("myCustomter",function(){ return {//在这里定义指令 restrict:"E", scope:{//隔离作用域 customer:'=customer'//绑定变量 }, template:"<div>" + "name:<span> {{customer.name}}</span><br>"//这里就是从隔离作用域中寻找变量 +"phoneNumber:<span>{{customer.phoneNumber}}</span>" +"</div>" }; });
然后就可以如下使用指令了:
<!doctype html> <html ng-app="app"> <head> <script src="js/angular.js"></script> <script src="js/default.js"></script> </head> <body> <div ng-controller="parentController"> <my-customter customer="customer1"></my-customter> <my-customter customer="customer2"></my-customter> </div> </body> </html>
结果如下:
name: GAMELoft9
phoneNumber:123456
name: vivian
phoneNumber:999999
可以看到,有了隔离作用域,自定义指令的可复用性就大大提高了!自定义指令有了隔离作用域,就不需要直接同外面controller打交道,而是通过指令的属性间接的通controller交互。这里是不是有一点面向接口编程的味道了?通过属性这个“接口”定义一些操作,然后通过控制器去实现这个操作。然后隔离作用域就可以使用,而不用关心是哪个控制器来实现的!如下图所示:
其中隔离作用域中的变量绑定到属性名称,属性中的值为字符串值、控制器中的变量或者函数。隔离作用域和属性的之间的绑定策略有如下三种:
@:绑定的属性值为字符串。此时没有与controller交互。
=:绑定的属性值为变量。此时与controller有交互。
&:绑定的属性值为函数。此时与controller有交互。
如果隔离作用域的变量名称和指令属性名称相同,则可以省略后面的属性名称。请看下面的例子:
var myApp = angular.module('app', []); myApp.controller("parentController", function ($scope) { $scope.customer1={//用户信息 name:"GAMELoft9", phoneNumber:"123456" }; $scope.customer2={//用户信息 name:"vivian", phoneNumber:"999999" }; $scope.saveCustomer= function(customer){ alert(customer.name + 'has been saved!'); }; }).directive("myCustomter",function(){ return {//在这里定义指令 restrict:"E", scope:{//隔离作用域 no:'@',//绑定字符串 customer:'=',//绑定变量 onSave:'&'//绑定函数 }, template:"<div>" + "no:<span>{{no}}</span><br>"+ "name:<span> {{customer.name}}</span><br>"//绑定了用户数据 +"phoneNumber:<span>{{customer.phoneNumber}}</span>" +"<input type='button' value='save' ng-click='onSave()'>" +"</div>" }; });
<!doctype html> <html ng-app="app"> <head> <script src="js/angular.js"></script> <script src="js/default.js"></script> </head> <body> <div ng-controller="parentController"> <my-customter no="01" customer="customer1" on-save="saveCustomer(customer1)"></my-customter> </div> </body> </html>
结果如下:
no:01
name: GAMELoft9
phoneNumber:123456
点击save后,会弹出“GAMELoft9 has been saved!”
其中no是绑定的字符串值,customer绑定是变量,onsave绑定的是函数。
之前我们有使用ng-controller来使用控制器,其实也可以在指令内部指定控制器,而且这种方式更好,如下所示:
var myApp = angular.module('app', []); myApp.controller("parentController", function ($scope) { $scope.customer1={//用户信息 name:"GAMELoft9", phoneNumber:"123456" }; }).controller("controllerOne", function ($scope) { $scope.customer3={//用户信息 name:"tom", phoneNumber:"888888" }; }).directive("myCustomter",function(){ return {//在这里定义指令 restrict:"E", controller:"controllerOne",//指定使用哪个控制器 template:"<div>" + "no:<span>{{no}}</span><br>"+ "name:<span> {{customer3.name}}</span><br>"//绑定了用户数据 +"phoneNumber:<span>{{customer3.phoneNumber}}</span>" +"</div>" }; });
而且还可以自己指定一个供内部使用的控制器:
var myApp = angular.module('app', []); myApp.controller("parentController", function ($scope) { $scope.customer1={//用户信息 name:"GAMELoft9", phoneNumber:"123456" }; }).directive("myCustomter",function(){ return {//在这里定义指令 restrict:"E", controller: function ($scope,$element,$attrs) { $scope.customer4={ name:"jack", phoneNumber:"7777777" }; }, template:"<div>" + "no:<span>{{no}}</span><br>"+ "name:<span> {{customer4.name}}</span><br>"//绑定了用户数据 +"phoneNumber:<span>{{customer4.phoneNumber}}</span>" +"</div>" }; });
这是一个仅供内部使用的控制器,是一个匿名函数。这里在控制器的函数中使用了$scope等服务,其实可以不用,如下:
controller: function () { this.customer4={ name:"jack", phoneNumber:"7777777" }; }, controllerAs:"innerController",
我们使用了controllerAs给控制器取了一个别名,这样在作用域中也可进行访问:
template:"<div>" + "no:<span>{{no}}</span><br>"+ "name:<span> {{innerController.customer4.name}}</span><br>"//绑定了用户数据 +"phoneNumber:<span>{{innerController.customer4.phoneNumber}}</span>" +"</div>"
这种方式的好处是控制器中没有注入$scope,同$scope是隔离的,易于测试。
和控制器类似的还有link函数,它也可以进行DOM操作。
angular.module("my-button", []).controller("buttonController", ["$scope", "$location", function($scope, $location) { $scope.button = { buttonText: "添加缴费账单", onButtonClick: function() { $location.path("input"); } }; }).directive("myButton", function() {function link(scope, elem, attrs, ctrl) { scope.model.buttonText = attrs.buttonText || scope.model.buttonText; scope.model.onButtonClick = scope.model.onButtonClick || function() { //指定别的操作DOM的代码 }; } return { restrict: "E", replace: true, scope: { model: "=" }, template: '<div>' + '<div 'ng-click="model.onButtonClick();">' + '{{model.buttonText}}' + '</div>' + '</div>', link: link }; });
指令的控制器和link函数可以进行互换。控制器主要是用来提供可在指令间复用的行为,而链接函数只能在当前内部指令中定义行为,且无法在指令间复用。link函数可以将指令互相隔离开来,而controller则定义可复用的行为。由于指令可以require其他指令所使用的控制器,因此控制器常被用来放置在多个指令间共享的动作。
如果我们希望将当前指令的API暴露给其他指令使用,可以使用controller参数,否则可以使用link来构造当前指令元素的功能性。如果我们使用了scope.$watch()或者想要与DOM元素做实时的交互,使用link函数会是更好的选择。技术上讲,$scope会在DOM元素被实际渲染之前传入到控制器中。在某些情况下,例如使用了嵌入,控制器中的作用域所反映的作用域可能与我们所期望的不一样,这种情况下,$scope对象无法保证可以被正常更新。当想要同当前屏幕上的作用域交互时,可以使用被传入到link函数中的scope参数。
相关文章推荐
- AngularJS学习(一)——基本的入门程序(配置环境)
- [angularjs] angularjs系列笔记(一)简介
- AngularJS实现鼠标右键事件
- 从 AngularJS 模块定义说起
- AngularJS介绍 - 下一个大框架
- angularjs---思维导图 01
- 细说Angular ng-class
- AngularJS入门(一)
- 我的angularjs源码学习之旅3——脏检测与数据双向绑定
- 安装Yeoman + Bower + Grunt 创建angularjs 项目结构(新手笔记一)
- AngularJS UI 扩展 AngularUI
- asp.net mvc+angularjs+web api单页应用
- AngularJS入门
- AngularJS 中的Promise --- $q服务详解
- [POJ] 1948 Triangular Pastures (DP)
- js框架 AngularJS+Bootstrap
- AngularJS vs. jQuery,看看谁更胜一筹
- AngularJS in Action读书笔记4(实战篇)——创建Statistic模块
- 在Asp.Net MVC中使用AngularJS的路由
- Angular之双向数据绑定基础