angular.module('docsIsolateScopeDirective', []) .controller('Ctrl', function($scope) { $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' }; $scope.igor = { name: 'Igor', address: '123 Somewhere' }; }) .directive('myCustomer', function() { return { restrict: 'E', scope: { customer: '=customer' }, templateUrl: 'my-customer.html' }; });
my-customer.html
Name: {{customer.name}} Address: {{customer.address}}
首先看hmtl,第一个<my-customer>绑定内部作用域的customer到naomi。这个naomi我们在控制器中已经定义好了。第二个是绑定customer到igor。
现在看看scope是如何配置的。
//... scope: { customer: '=customer' }, //...
属性名(customer)是myCustomer指令上isolated scope的变量名。它的值(=customer)告诉编译器绑定到customer属性。
Note: 指令作用域配置中的'=attr'属性名是被规范化过后的名字,比如要绑定<div bind-to-this="thing">中的属性,你就要使用'=bindToThis'的绑定。
对于属性名和你想要绑定的值的名字一样,你可以使用这样的快捷语法:
//... scope: { // same as '=customer' customer: '=' }, //...
使用isolated scope还有另外一个用处,那就是可以绑定不同的数据到指令内部的作用域。
在我们的例子中,我们可以添加另外一个属性vojta到我们的作用域,然后在我们的指令模板中访问它。
<div ng-controller="Ctrl"> <my-customer customer="naomi"></my-customer> </div>
JS
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: { customer: '=customer' }, templateUrl: 'my-customer-plus-vojta.html' }; });
my-customer-plus-vojta.html
Name: {{customer.name}} Address: {{customer.address}} <hr> Name: {{vojta.name}} Address: {{vojta.address}}
注意到,{{vojta.name}}和{{vojta.address}}都是空的,意味着他们是undefined, 虽然我们在控制器中定义了vojta,但是在指令内部访问不到。
就像它的名字暗示的一样, 指令的isolate scope隔离了除了你添加到作用域:{}对象中的数据模型外的一切东西。这对于你要建立一个可重复使用的组件是非常有用的,因为它阻止了除了你想要传入的数据模型外其他东西改变你数据模型的状态。
Note: 正常情况下,作用域是原型继承自父作用域。但是isolate scope没有这样的继承。
Best Practice: 当你想要使你的组件在应用范围内可重用,那么使用scope配置去创建一个isolate scopes
创建一个操作DOM的指令
在这个例子中,我们会创建一个显示当前时间的指令,每秒一次更新DOM以正确的显示当前的时间。
指令修改DOM通常是在link配置中,link选项接受一个带有如下标签的函数function link(scope,element,attrs) {...}其中:
-scope是angular scope对象
-element指令匹配的jqLite封装的元素(angular内部实现的类jquery的库)
-attrs是一个带有规范化后属性名字和相应值的对象
在我们的link函数中,我们更新显示时间每秒一次,或者当用户改变指定绑定的时间格式字符串的时候。我们也要移除定时器,当指令被删除的时候,以避免引入内存泄露。
<div ng-controller="Ctrl2"> Date format: <input ng-model="format"> <hr/> Current time is: <span my-current-time="format"></span> </div>
JS
angular.module('docsTimeDirective', []) .controller('Ctrl2', function($scope) { $scope.format = 'M/d/yy h:mm:ss a'; }) .directive('myCurrentTime', function($timeout, dateFilter) { function link(scope, element, attrs) { var format, timeoutId; function updateTime() { element.text(dateFilter(new Date(), format)); } scope.$watch(attrs.myCurrentTime, function(value) { format = value; updateTime(); }); function scheduleUpdate() { // save the timeoutId for canceling timeoutId = $timeout(function() { updateTime(); // update DOM scheduleUpdate(); // schedule the next update }, 1000); } element.on('$destroy', function() { $timeout.cancel(timeoutId); }); // start the UI update process. scheduleUpdate(); } return { link: link }; });
这里有很多东西值得注意的,就像module.controller API, module.directive中函数参数是依赖注入,因此,我们可以在Link函数内部使用$timeout和dataFilter服务。
我们注册了一个事件element.on('$destroy', ...), 是什么触发了这个$destory事件呢?