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

在指令中操作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>

运行效果:

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

虽然修改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!

时间: 2024-12-18 23:22:56

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

AngularJS自定义指令详解(有分页插件代码)

前言 除了 AngularJS 内置的指令外,我们还可以创建自定义指令. 通过 .directive() 函数来添加自定义的指令. 调用自定义指令时,需要在HTMl 元素上添加自定义指令名. 自定义指令命名规则:使用驼峰命名法来命名,即除第一个单词外的首字母需大写.如: myDirective. 在html页面调用该指令时需要以 - 分割,如: my-directive.示例代码: <body ng-app="myApp"> <my-directive><

AngularJS 自定义指令详解

版权声明:本文为博主原创文章,未经博主允许不得转载. //blog.csdn.net/qq_27626333/article/details/52261409 除了 AngularJS 内置的63个指令外,我们还可以创建自定义指令.你可以使用 .directive 函数来添加自定义的指令.要调用自定义指令,HTML 元素上需要添加自定义指令名.使用驼峰法来命名一个指令, runoobDirective, 但在使用它时需要以 - 分割, runoob-directive,自定义指令的参数如下: [

angular 自定义指令详解 Directive

在angular中,Directive,自定义指令的学习,可以更好的理解angular指令的原理,当angular的指令不能满足你的需求的时候,嘿嘿,你就可以来看看这篇文章,自定义自己的指令,可以满足你的各种需求的指令. 本篇文章的参考来自  AngularJS权威指南 , 文章中主要介绍指令定义的选项配置 废话不多说,下面就直接上代码 //angular指令的定义,myDirective ,使用驼峰命名法 angular.module('myApp', []) .directive('myDi

day3 自定义指令详解

在angular中,Directive,自定义指令的学习,可以更好的理解angular指令的原理,当angular的指令不能满足你的需求的时候,嘿嘿,你就可以来看看这篇文章,自定义自己的指令,可以满足你的各种需求的指令. 本篇文章的参考来自  AngularJS权威指南 , 文章中主要介绍指令定义的选项配置 废话不多说,下面就直接上代码 //angular指令的定义,myDirective ,使用驼峰命名法 angular.module('myApp', []) .directive('myDi

angularJs之指令详解(一)

指令(Directives)是所有AngularJS应用最重要的部分.尽管AngularJS已经提供了非常丰富的指令,但还是经常需要创建应用特定的指令.这篇教程会为你讲述如何自定义指令,以及介绍如何在实际项目中使用.在这篇文章的最后(第二部分),我会指导你如何使用Angular指令来创建一个简单的记事本应用. 概述 一个指令用来引入新的HTML语法.指令是DOM元素上的标记,使元素拥有特定的行为.举例来说,静态的HTML不知道如何来创建和展现一个日期选择器控件.让HTML能识别这个语法,我们需要

angular的自定义指令---详解

1.angualr指令 在angualr自己里面有许多丰富的指令,但都是平时所常见的,但对于自己所需要的可能有所欠缺,所以自己可能会摒弃原声指令,自己封装更为健壮灵活的指令: 其实angular里面的指令也是基于下面所用到的步骤来创建的,只不过他都添加到了全局中,所以可以直接使用: 2.创建自定义指令 首先创建模块app,再使用app的服务directive方法, 创建指令内容第一个参数自定义的名字,第二个参数是自定义参数属性对象,该对象包括的属性基本在代码注释解释清楚: // a.创建模块 v

angular 自定义指令 详解--restrict、restrict、replace

Angularjs 允许根据实际业务需要自定义指令, 通过angular全局对象下的 directive 方法实现.可以自定义属性.自定义标签.自定义功能 接下来定义一个名叫custom的指令,并利用这个自定义指令来实现元素的替换 html代码: <body ng-app="app"> <p custom></p> <custom></custom> js代码: var app = angular.module("a

Vue.js 源码分析(二十三) 高级应用 自定义指令详解

除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令. 官网介绍的比较抽象,显得很高大上,我个人对自定义指令的理解是:当自定义指令作用在一些DOM元素或组件上时,该元素在初次渲染.插入到父节点.更新.解绑时可以执行一些特定的操作(钩子函数() 自定义指令有两种注册方式,一种是全局注册,使用Vue.directive(指令名,配置参数)注册,注册之后所有的Vue实例都可以使用,另一种是局部注册,在创建Vue实例时通过directives属性创建局部指令,局

vue2.0 自定义指令详解

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script src="vue2.2.js"></script> </head> <body> <div id="app"> <div v-lang="color