AngularJs自定义指令详解(5) - link

时间:2022-09-02 16:32:53

在指令中操作DOM,我们需要link参数,这参数要求声明一个函数,称之为链接函数。

写法:

link: function(scope, element, attrs) {
  // 在这里操作DOM
}

如果指令使用了require选项,那么链接函数会有第四个参数,代表控制器或者所依赖的指令的控制器。

// require 'SomeController',
link: function(scope, element, attrs, SomeController) {
  // 在这里操作DOM,可以访问required指定的控制器
}

链接函数之所以能够在指令中操作DOM,就是因为传入的这几个参数:scope、element、attrs

scope:即与指令元素相关联的当前作用域,可以用来注册监听器:scope.$watch()

element:即当前指令对应的元素,使用它可以操作该元素及其子元素。例如<span my-directive></span>,这个span就是指令 my-directive所使用的元素。

attrs:由当前元素的属性组成的对象。

下面看一个例子,来自官方文档的示例。

我们要实现一个时钟,根据给定的时间格式显示当前的时间,而且每隔一秒要更新一次时间。

首先在控制器中初始化一个时间格式:

controller('Controller', ['$scope', function($scope) {
    $scope.format = 'M/d/yy h:mm:ss a';
}])

对于时间格式,显然我们要引入$filter服务。

对于”每隔一秒“进行某些操作,显然要引入$interval服务。

为了测试程序,我们还引入$log服务以便在浏览器中观察输出。

所以自定义的指令需要写成这样:

directive('myClock', ['$interval', '$filter', '$log', function($interval, $filter,$log)

这个myClock指令将会被注入$interval、$filter、$log服务。

刷新时间显示,也就是要求我们在指令中操作DOM,输出时间:

function updateTime() {
  element.text($filter('date')(new Date(), interFormat));
}

$filter方法的使用:

$filter('date')(date, format, timezone)

参考https://code.angularjs.org/1.3.16/docs/api/ng/filter/date

每隔一秒刷新显示:

$interval(

  function() {
    updateTime();
  },

  1000

);

完整代码如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="../lib/angular-1.3.16/angular.min.js"></script>
<script src=""></script>
<title></title>
<script language="JavaScript">
angular.module('docsTimeDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
}])
.directive('myClock', ['$interval', '$filter', '$log', function($interval, $filter,$log) {
return {
scope:{
myFormat:'='
},
link: function(scope, element, attrs) {
function updateTime() {
element.text($filter('date')(new Date(), scope.myFormat));
}
updateTime();
$interval(function() {
updateTime();
}, 1000);
}
};
}]);
</script>
</head>
<body ng-app="docsTimeDirective">
<div ng-controller="Controller">
时间格式: <input ng-model="format"> <hr/>
当前时间: <span my-clock my-format="format"></span>
</div>
</body>
</html>

运行效果:
AngularJs自定义指令详解(5) - link

不过我们很快就发现一个问题,就是修改时间格式后,无法立刻刷新时间显示,把每隔一秒修改为每隔五秒,问题就更加明显了。

虽然修改format会因为双向绑定而使myFormat发生变化,但是后者并不会触发执行updateTime()函数。

所以需要加入$watch监听:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="../lib/angular-1.3.16/angular.min.js"></script>
<script src=""></script>
<title></title>
<script language="JavaScript">
angular.module('docsTimeDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
}])
.directive('myClock', ['$interval', '$filter', '$log', function($interval, $filter,$log) {
return {
scope:{
myFormat:'='
},
link: function(scope, element, attrs) {
function updateTime() {
element.text($filter('date')(new Date(), scope.myFormat));
}
scope.$watch('myFormat',function(newValue) {
$log.info('value changed to ' +
newValue);
updateTime();
});

$interval(function() {
updateTime();
}, 1000);
}
};
}]);
</script>
</head>
<body ng-app="docsTimeDirective">
<div ng-controller="Controller">
时间格式: <input ng-model="format"> <hr/>
当前时间: <span my-clock my-format="format"></span>
</div>
</body>
</html>

注意$watch()的第一个参数为'myFormat',不要少了单引号,也不要写成'scope.myFormat'、'$scope.myFormat',要不然newValue的值是undefined了。

还有就是删掉了外面的updateTime()调用,因为在$watch里myFormat第一次绑定时,已经触发监听器的回调函数了,于是updateTime()也立刻执行。

上面的代码监听的是定义在指令的隔离作用域上的myFormat,而官方文档监听的是DOM中span元素的my-format属性,效果是差不多的,代码如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="../lib/angular-1.3.16/angular.min.js"></script>
<script src=""></script>
<title></title>
<script language="JavaScript">
angular.module('docsTimeDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
}])
.directive('myClock', ['$interval', '$filter', '$log', function($interval, $filter,$log) {
return {
link: function(scope, element, attrs) {
var interFormat, timeoutId; function updateTime() {
element.text($filter('date')(new Date(), interFormat));
} scope.$watch(attrs.myFormat, function(value) {
interFormat = value;
updateTime();
}); element.on('$destroy', function() {
$interval.cancel(timeoutId);
});

timeoutId = $interval(function() {
updateTime();
}, 1000);
}
};
}]);
</script>
</head>
<body ng-app="docsTimeDirective">
<div ng-controller="Controller">
时间格式: <input ng-model="format"> <hr/>
Current time is: <span my-clock my-format="format"></span>
</div>
</body>
</html>

官方文档指出一个问题:$interval注册的匿名函数不会在元素被移除时自动释放,存在一定的内存泄露风险,所以增加了代码:

element.on('$destroy', function() {
  $interval.cancel(timeoutId);
});

这三行代码也演示了如何在指令内加入对元素的事件监听器,官方文档还提供了另一个例子,代码如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="../lib/angular-1.3.16/angular.min.js"></script>
<script src=""></script>
<title></title>
<script language="JavaScript">
angular.module('dragModule', [])
.directive('myDraggable', ['$document', function($document) {
return {
link: function(scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0; element.css({
position: 'relative',
border: '1px solid blue',
backgroundColor: 'yellow',
cursor: 'pointer'
}); element.on('mousedown', function(event) {
// Prevent default dragging of selected content
event.preventDefault();
startX = event.pageX - x;
startY = event.pageY - y;
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
}); function mousemove(event) {
y = event.pageY - startY;
x = event.pageX - startX;
element.css({
top: y + 'px',
left: x + 'px'
});
} function mouseup() {
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
}
};
}]);
</script>
</head>
<body ng-app="dragModule">
<span my-draggable>Drag ME</span>
</body>
</html>

现在回顾一下前面文章提到的隔离作用域问题,看看以下代码:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="../lib/angular-1.3.16/angular.min.js"></script>
<script src=""></script>
<title></title>
<script language="JavaScript">
angular.module('app',[])
.directive('myDirective',function(){
return{
template:'Hello {{greeting}}!',
//scope:{ },
link:function(scope,element,attrs){
scope.greeting = 'AngularJs';
}
};
});
</script>
</head>
<body ng-app="app">
<div ng-init="greeting='World'"></div>
1,<span>Hello {{greeting}}!</span><hr>
2,<span my-directive></span><hr>
</body>
</html>

输出:

1,Hello AngularJs!


2,Hello AngularJs!

虽然greeting被初始化为'World',但是在链接函数里修改成'AngularJs’,可见此时传给链接函数的scope是上一级作用域(在这里是rootScope)

这造成了污染,一般情况下我们不希望指令不声不响地修改外面的变量,解决办法是把代码里//scope:{}的注释去掉,隔离指令的作用域。

于是输出就会变成:

1,Hello World!


2,Hello AngularJs!

再看下面的代码:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="../lib/angular-1.3.16/angular.min.js"></script>
<script src=""></script>
<title></title>
<script language="JavaScript">
angular.module('app',[])
.directive('myDirective',function(){
return{
restrict:'E',
template:'<span ng-transclude></span>',
scope:{ },
transclude: true,
link:function(scope,element,attrs){
scope.greeting = 'AngularJs';
}
};
});
</script>
</head>
<body ng-app="app">
<div ng-init="greeting='World'"></div>
1,<span>Hello {{greeting}}!</span><hr>
2,<my-directive>Hello {{greeting}}!</my-directive><hr>
</div>
</body>
</html>

输出:

1,Hello World!


2,Hello World!

即使ng-transclude指令放在指令定义的模板中,但是{{greeting}}绑定放在外面,而指令已经隔离了作用域,所以{{greeting}}使用的是外面的'World'。

如果注释掉scope:{},指令的作用域没有隔离,于是输出就变为:

1,Hello AngularJs!


2,Hello AngularJs!

AngularJs自定义指令详解(5) - link的更多相关文章

  1. AngularJs自定义指令详解(1) - restrict

    下面所有例子都使用angular-1.3.16.下载地址:http://cdn.bootcss.com/angular.js/1.3.16/angular.min.js 既然AngularJs快要发布 ...

  2. AngularJs自定义指令详解(6) - controller、require

    在前面文章中提到一旦声明了require,则链接函数具有第四个参数:controller. 可见require和controller是配合使用的. 在自定义指令中使用controller,目的往往是要 ...

  3. AngularJs自定义指令详解(10) - 执行次序

    代码: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8 ...

  4. AngularJs自定义指令详解(2) - template

    一些用于定义行为的指令,可能不需要使用template参数. 当指定template参数时,其值可以是一个字符串,表示一段HTML文本,也可以是一个函数,这函数接受两个参数:tElement和tAtt ...

  5. AngularJs自定义指令详解(9) - terminal

    例子: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8 ...

  6. AngularJs自定义指令详解(8) - priority

    priority 默认值为0. 当一个元素上声明两个指令,而且它们的priority一样,谁先被调用?这个需要分情况讲.下面先给个例子: <!DOCTYPE html> <html& ...

  7. AngularJs自定义指令详解(7) - multiElement

    multiElement不太常用,从下面这个例子可以大致看出它的作用: <!DOCTYPE html> <html> <head lang="en"& ...

  8. AngularJs自定义指令详解(3) - scope

    我们之所以要定义指令,目的是重用指令.假设有这么一个应用场景:在同一个html里使用了两次my-directive,第一个my-directive要展示的是Hello World,第二个my-dire ...

  9. AngularJs自定义指令详解(4) - transclude

    transclude默认值为false,如果设置 transclude为true,那么相应地,必须在模板代码中加入ng-transclude指令. 先看个例子: <!DOCTYPE html&g ...

随机推荐

  1. CDN技术

    CDN 是构建在数据网络上的一种分布式的内容分发网. CDN 的作用是采用流媒体服务器集群技术,克服单机系统输出带宽及并发能力不足的缺点,可极大提升系统支持的并发流数目,减少或避免单点失效带来的不良影 ...

  2. codeforces 385 c

    Description Recently, the bear started studying data structures and faced the following problem. You ...

  3. POJ 1845 求a&Hat;b的约数和

    题目大意就是给定a和b,求a^b的约数和 f(n) = sigma(d) [d|n] 这个学过莫比乌斯反演之后很容易看出这是一个积性函数 那么f(a*b) = f(a)*f(b)  (gcd(a,b) ...

  4. 2440裸 Delay&lpar;&rpar;&semi; 和 while&lpar;&excl;&lpar;rUTRSTAT0 &amp&semi;amp&semi; 0x2&rpar;&rpar;&semi; 问题

    前两天写RTC中断 使用串行输出 它发现,该方案将while(!(rUTRSTAT0 & 0x2));走不出的情况.的 解决方法: main函数添加: U32 mpll_val = 0,con ...

  5. u-boot源码下载

    1. 使用git下载u-boot源码  1.1 clone u-boot源码仓库 可以使用git.HTTP.和 rsync协议来下载u-boot源码.你可以使用如下方法来克隆源码树: $ git cl ...

  6. Linux Ubuntu从零开始部署web环境及项目-----搭建ssh环境&lpar;一&rpar;

    linux搭建ssh环境 1,用户登录 成功输入用户名和密码后 进入Ubuntu界面  2,配置网络 参考:http://blog.csdn.net/liu782726344/article/deta ...

  7. MT【332】椭圆正交变换

    (2018河南数学联赛解答10) 已知方程$17x^2-16xy+4y^2-34x+16y+13=0$表示椭圆,求它的对称中心和对称轴. 解:设对称中心为$(a,b)$,显然$A(1,1),B(1,- ...

  8. pytesseract使用的坑

    今天学了下python的OCR识别,其中遇到好多坑,下面就一一阐述是如何破解的,本人用的是Windows 64位,IDE是VS2017. pip版本过低. 首先安装pytesseract这个库,pip ...

  9. 移动端web兼容各种分辨率写法

    移动端web开发最好用rem单位,再设置以下js,以iphone6 750*1334为基准 <script> var size = document.documentElement.cli ...

  10. &lt&semi;context&colon;component-scan&gt&semi;自动扫描

    主要讲解自动扫描的<context:component-scan>中的2个子标签的使用方法 在Spring MVC中的配置中一般会遇到这两个标签,作为<context:compone ...