文档章节

AngularJS中的指令(自定义指令)

GlassHeart
 GlassHeart
发布于 2017/03/30 15:36
字数 3063
阅读 27
收藏 0

AngularJS自定义指令在HTML代码中可以有四种表现形式:

1、作为一个新的HTML元素来使用。

<hello></hello>或者<hello/>

2、作为一个元素的属性来使用

<div hello></div>

3、作为一个元素的类来使用

<div class="hello"></div>

4、作为注释来使用

<!--directive: hello -->
注意:"directive: hello"的后面要有一个空格,不然没效果,注释的方式建议少用。

自定义指令的定义:

.directive('hello',function(){
    return {
        restrict:'AECM',
        template:'<button>click me</button>'
    }
})

上面是定义一个指令最简单的代码,。在上面的代码中,directive()这个方法定义了一个新的指令,该方法有两个参数,第一个'hello'就是规定指令的名字为hello,第二个参数是返回指令对象的函数。上面的代码中,该函数主要使用了两个属性来定义这个hello指令:

  • restrict[string]这个属性,主要是用来规定指令在HTML代码中可以使用什么表现形式。A代表属性、E代表元素、C代表类、M代表注释。实际情况中我们一般使用AE这两种方式。
  • template[string or function]这个属性,规定了指令被Angular编译和链接(link)后生成的HTML标记,这个属性可以简单到只有一个HTML文本在里面,也可以特别复杂,当该属性的值为function的时候,那么该方法返回的就是代表模板的字符串,同时也可以在里面使用{{}}这个表达式。
  • template: function () {
        return '<button>click me</button>';
    }

一般情况下,template这个属性都会被templateUrl取代掉,用它来指向一个外部的文件地址,所以我们通常把模板放在外部的一个HTML文件中,然后使用templateUrl来指向他。

在定义指令的时候,除了以上两个最基础的属性外,我们还会使用到其他的很多属性:

  • priority[number]属性,这个属性是来规定自定义的指令的优先级的,当一个DOM元素上面有一个以上的指令的时候,就需要去比较指令的优先级了,优先级高的指令先执行。这个优先级就是用来在执行指令的compile函数前,先排序的。
  • terminal[boolean]属性,该参数用来定义是否停止当前元素上比本指令优先级低的指令,如果值为true,就是正常情况,按照优先级高低的顺序来执行,如果设置为false,就不会执行当前元素上比本指令优先级低的指令。
  • replace[boolean]属性,这个属性用来规定生成的HTML内容是否会替换掉定义此指令的HTML元素。当我们把该属性的值设为true的时候<button>点击我</button>,当我们设置为false的时候会是这样的<hello><button>点击我</button></hello>
  • link[function]属性,在上面的例子中,我们自定义的指令其实没有多大意义,这只是一个最简单的指令,有好多的属性我们都没有为他定义,所以没有多大用途。比如这个link函数主要是用来添加对DOM元素的事件监听、监视模型属性变化、以及更新DOM的。它包括三个参数:scope、element、attrs

(1)、scope参数,在我们没有为指令定义scope属性的时候,那么他代表的就是父controller的scope。

(2)、element参数,就是指令的jQLite(jQuery的子集)包装DOM元素。如果你在引入AngularJS之前引入了jQuery,那么这个元素就是jQuery元素,而不是jQLite元素。由于这个元素已经被jQuery/jQLite包装了,所以我们就在进行DOM操作的时候就不需要再使用 $()来进行包装。

(3)、attrs参数,它包含了该指令所在元素的属性的标准化参数对象。

  • scope[boolean or object]属性,该属性是用来定义指令的scope的范围,默认情况下是false,也就是说该指令继承了父controller的scope,可以随意的使用父controller的scope里的属性,但是这样的话就会污染到父scope里的属性,这样是不可取的。所以我们可以让scope取以下两个值:true和{}。

(1)、当为true的时候,表示让Angular给指令创建一个继承于父scope的scope

  • var myapp=angular.module('myapp',[])
        .controller('myctrl',['$scope', function ($scope) {
            $scope.color='red';
        }])
        .directive('hello', function () {
            return{
                restrict:'AECM',
                replace:true,
                template:'<button ng-click="sayhello()" '+'
                          style="background-color: {{color}}">点击我</button>',
                scope:true,
                link: function (scope,elements,attrs) {
                    elements.bind('click', function () {
                        elements.css('background-color','blue');
                    })
                }
            }
        })

这里我们为父scope定义了一个color的属性,并赋值为red,在hello指令的scope属性中,我们给了true,所以angular就为这个指令创建了一个继承于父scope的scope,然后在template属性中,我们用{{}}使用了从父scope中继承过来的color属性,所以按钮会是红色的。

当为{}的时候,表示创建一个隔离的scope,不会继承父scope的属性。但是在有的时候我们也要需要访问父scope里的属性或者方法,那么我们应该怎么办呢。angular早就为我们想到了这一点,有以下的三个办法可以让我们记住上面的操作:

使用@实现单向绑定,如果我们只给scope的这个{}值的话,那么上面代码的按钮的背景色将会是灰色的。而如果我们需要使用父scope的color属性的时候,我们可以这样写:

scope:{
    color:'@color'
}

<hello color="{{color}}"></hello>

注意:1、scope里的属性color代表的是模板{{}}这个表达式里面的color,两者必须一致。2、scope里的属性color的值,也就是@后面的color,表示的是下面的HTML元素里的属性color,所以这两者也必须一致,当这里的属性名和模板里表达式{{}}里面使用的名称相同的话,就可以省略掉@后面的属性名了。

写成:

scope:{
    color:'@'
}

从指令中scope的值可以看出,指令模板中的表达式{{}}里的color的指向的是当前元素元素的color属性,这个color属性的值就是父scope的属性color的值。父scope把他的color属性值传递给了当前元素的color属性,然后color属性又把值传递给了模板中表达式里的color,这个过程是单向的。

(2)、使用=实现双向绑定

.directive('hello', function () {
    return{
        restrict:'AECM',
        replace:true,
        template:'<button style="background-color: {{color}}">点击我</button>',
        scope:{
            color:'='
        },
        link: function (scope,elements,attrs) {
            elements.bind('click', function () {
                elements.css('background-color','blue');
                scope.$apply(function () {
                    scope.color='pink';
                })
            })
        }
    }
})


<hello color="color"></hello> 
<input type="text" ng-model="color"/>

这里我们给指令的scope中的color属性和父scope中的color属性进行了双向绑定,并且给指令的link函数里,添加了一个单击事件,点击按钮会让按钮的颜色发生变化,并且改变指令scope的color属性的值,再给HTML页面中加了一个input标签,输出或者输入父scope的color属性的值。这里有一个地方需要注意:当前元素的属性名的值不用再加上{{}}这个表达式了,因为这里父scope传递的是一个真实的scope数据模型,而不是简单的字符串,所以这样我们就可以传递简单的字符串、数组、甚至复杂的对象给指令的scope。

点击我们能看到,按钮的颜色变成了粉色的,说明点击让指令的scope的color属性发生了变化,从而导致按钮的颜色发生了变化。但是这里不仅仅是按钮发生了变化,input表单里的值也变成了pink,这就说明父scope的color属性也发生了变化。

当我们在input框里输入另外一种颜色的时候,按钮的颜色也发生了变化,这就说明指令的scope的color属性被改变了。综上我们可以发现使用'='实现的是双向绑定。

(3)、使用&调用父scope里的方法

var myapp=angular.module('myapp',[])
    .controller('myctrl',['$scope', function ($scope) {
        $scope.color='red';
        $scope.sayhello= function () {
            alert('hello');
        };
    }])
    .directive('hello', function () {
        return{
            restrict:'AECM',
            replace:true,
            template:'<button ng-click="sayhello()" style="background-color: {{color}}">点击我</button>',
            scope:{
                color:'=',
                sayhello:'&'
            },
            link: function (scope,elements,attrs) {
                elements.bind('click', function () {
                    elements.css('background-color','blue');
                    scope.$apply(function () {
                        scope.color='pink';
                    })
                })
            }
        }
    })


<hello color="color" sayhello="sayhello()"></hello>
<input type="text" ng-model="color"/>

注意:1、我们不仅需要在模板中使用ng-click指令,绑定上要调用的父scope中的方法,而且要在给当前元素添加一个属性,并且这个属性指向要调用的父scope的方法。2、指令scope的属性sayhello、当前元素的属性sayhello、模板绑定的事件方法名sayhello这三者要一致。那么这样我们就可以点击按钮,弹出一个对话框了。

  • transclude[boolean]属性,这个属性用来让我们规定指令是否可以包含任意内容
    .directive('hello', function () {
        return{
            restrict:'AECM',
            replace:true,
            transclude:true,
            scope:{},
            template:'<div ng-transclude></div>',
        }
    })
    
    
    <hello> hello <span>{{color}}</span> </hello>

当他的值为true的时候,这时页面上输出hello red。当为false的时候,页面上将会是空白的。注意<span>{{color}}</span>的color是父scope里的color。并不是指令里的scope的color属性。

  • compile[function]参数,该方法有两个参数element,attrs,第一个参数element指指令所在的元素,第二个attrs指元素上赋予的参数的标准化列表。这里我们也有个地方需要注意:compile 函数不能访问 scope,并且必须返回一个 link 函数。但是如果没有设置 compile 函数,你可以正常地配置 link 函数,(有了compile,就不能用link,link函数由compile返回)。
    .directive('hello', function () {
        return{
            restrict:'AECM',
            replace:true,
            translude:true,
            template:'<button ng-click="sayhello()" '+
                     'style="background-color: {{color}}">点击我</button>',
            scope:{
                color:'=',
                sayhello:'&'
            },
            compile: function (element,attrs) {
                return function (scope,elements,attrs) {
                    elements.bind('click', function () {
                        elements.css('background-color','blue');
                        scope.$apply(function () {
                            scope.color='pink';
                        })
                    })
                };
            }
        }

点击按钮之后发生的事情和前面用link属性的一样,其实是没有多少差别的。大多数的情况下,我们只需要使用 link 函数。这是因为大部分的指令只需要考虑注册事件监听、监视模型、以及更新DOM等,这些都可以在 link 函数中完成。 但是对于像 ng-repeat 之类的指令,需要克隆和重复 DOM 元素多次,在 link 函数执行之前由 compile 函数来完成。那么为什么我们需要两个分开的函数来完成生成过程,为什么不能只使用一个?需要理解指令在Angular中是如何被编译的!

  • 指令是如何被编译的

当我们的angular应用引导启动的时候,angular将会使用$compile服务遍历DOM元素,在所有的指令都被识别之后,将会调用指令的compile方法,返回一个link函数,然后将这个link函数添加到稍后执行的 link 函数列表中,这个过程被称为编译阶段。像ng-repeat这样的指令,需要被重复克隆很多次,compile函数只在编译阶段被执行一次,并且复制这些模板,但是link 函数会针对每个被复制的实例被执行。所以分开处理,让我们在性能上有一定的提高。

  • controller[string or function]和require[string or string[]]参数,当我们想要允许其他的指令和你的指令发生交互时,我们就需要使用 controller 函数。当另一个指令想要交互时,它需要声明它对你的指令 controller 实例的引用(require)。
    .directive('hello', function () {
        return{
            scope:{},
            require:'^he',
            compile: function (element,attrs) {
                return function (scope,elements,attrs,cntIns) {
                    cntIns.fn()
                };
            }
        }
    })
    
    
    .directive('he', function () {
        return {
            restrict:'AE',
            scope:{},
            controller: function ($scope, $compile, $http) {
                this.fn= function () {
                    alert('hello');
                };
            }
        }
    })
    
    
    
    <he> 
       <hello color="color" sayhello="sayhello()"></hello>
    </he>

当页面加载完毕之后,会弹出一个对话框。

© 著作权归作者所有

共有 人打赏支持
GlassHeart
粉丝 9
博文 26
码字总数 73400
作品 0
成都
前端工程师
私信 提问
《AngularJS学习整理》系列分享专栏

《AngularJS学习整理》系列分享专栏 《AngularJS学习整理》已整理成PDF文档,点击可直接下载至本地查阅 https://www.webfalse.com/read/201748.html 文章 教你用AngularJS框架一行JS代码实现...

开元中国2015
2018/11/09
0
0
【前端】—聊聊我认识的Angular

前言 最近接触的项目前端用到了Angular框架,之前略有耳闻,从vue换到Angular,感觉东西差不多,还是要系统学习的,先来了解下。 正文 1、Angular 的发展 AngularJS 是一款来自Google的前端J...

zt15732625878
2018/05/19
0
0
AngularJS 的自定义指令

这是一篇angularjs自定义指令的教程。就让我们来看看,如何在angularjs中自定义指令吧!我们将利用现有的guitar angularjs app,这在 教程14: AngularJ表单验证 中用到过。你可以在这里下载 ...

oschina
2014/07/28
13.5K
2
AngularJS应用开发思维之1:声明式界面

这篇博客之前承接上一篇:http://www.cnblogs.com/xuema/p/4335180.html 重写示例:模板、指令和视图 AngularJS最显著的特点是用静态的HTML文档,就可以生成具有动态行为的页面。 还是前面的...

笔阁
2015/03/16
0
0
开源中国的 AngularJS 优秀文章汇总

Angular JS (Angular.JS) 是一组用来开发Web页面的框架、模板以及数据绑定和丰富UI组件。它支持整个开发进程,提供web应用的架构,无需进行手工DOM操作。 AngularJS很小,只有60K,兼容主流浏...

oschina
2014/08/24
5.7K
13

没有更多内容

加载失败,请刷新页面

加载更多

Java单例模式学习记录

在项目开发中经常能遇见的设计模式就是单例模式了,而实现的方式最常见的有两种:饿汉和饱汉(懒汉)。由于日常接触较多而研究的不够深入,导致面试的时候被询问到后有点没底,这里记录一下学习...

JerryLin123
昨天
3
0
VSCODE 无法调试

VSCODE 无法调试 可以运行 可能的原因: GCC 的参数忘了加 -g

shzwork
昨天
4
0
理解去中心化 稳定币 DAI

随着摩根大通推出JPM Coin 稳定币,可以预见稳定币将成为区块链落地的一大助推器。 坦白来讲,对于一个程序员的我来讲(不懂一点专业经济和金融),理解DAI的机制,真的有一点复杂。耐心看完...

Tiny熊
昨天
4
0
5.线程实现

用于线程实现的Python模块 Python线程有时称为轻量级进程,因为线程比进程占用的内存少得多。 线程允许一次执行多个任务。 在Python中,以下两个模块在一个程序中实现线程 - _thread 模块 th...

Eappo_Geng
昨天
6
0
ServiceLoader

创建一个接口文件在resources资源目录下创建META-INF/services文件夹在services文件夹中创建文件,以接口全名命名创建接口实现类 内容me.zzp.ar.d.PostgreSQLDialectme.zzp.ar.d.Hype...

Cobbage
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部