输出:Hello Angular World !,可见先执行了ng-init="greeting='Hello '",然后执行了指令d2,最后是d1。实际我们想预期输出的是Hello World Angular !,我们将指令元素中的d1、d2顺序对调,再将指令中创建的d1、d2两个指令顺序对调,再试发现输出的还是Hello Angular World !,这是因为Angular是使用字母顺序来确定链接函数谁先被调用,将d1改为e1就会得到预期的结果,有兴趣的可以尝试一下。如何设定指令执行优先级?这时候priority就派上用场了。
Angular自定义指令Directive详解
时间:2020-02-09 17:02:31 编辑:Echeverra
1、Angular Directive说明
Directive中文翻译为指令,从《AngularJs权威教程》中对指令的描述,我理解的指令是对控制器的补充,主要功能是对Dom元素和数据的操作(一般指令主Dom操作、服务主数据操作,非强制规定),本文基于Angular1.4.6版本进行讲解和演示。
在Angular中内置了一些指令,如常见的
ng-app
指令初始化一个 AngularJS 应用程序,ng-model
指令把元素值(比如输入域的值)绑定到应用程序。同时Angular也支持创建自定义指令。2、Angular Directive使用
基础案例展示。
Html代码:
Js代码:
在线代码演示
这是一个很简单的指令小案例,指令中的
template
模板内容“这是自定义指令”通过<my-dir>
标签引入到页面中,涉及到指令两个很重要的参数template
和restrict
,下面会具体讲解指令的参数。3、Angular Directive参数
return {
pre: function(scope, iElement, iAttrs, controller) { ... },
post: function(scope, iElement, iAttrs, controller) { ... }
}
return function postLink(...) { ... }
}
已上是指令的所有参数以及类型,重点介绍常用参数
restrict、scope、template、templateUrl、replace、priority、link
。参数1:restrict
restrict用于指定directive的使用形式。如上文例子指令代码中的
restrict: "E"
即指名使用Element元素形式,同时还有A、C、M
共四种形式。默认值为EA
。<my-dir></my-dir>
<div my-dir></div>
<div class="my-dir"></div>
<!-- directive:my-dir -->
Html代码:
Js代码:
在线代码演示
指令命名需使用驼峰模式,Html引入指令需使用“-”连接每个单词,如指令
myDir
在引入指令时改为my-dir
写法。E、A、C、M
四种形式可组合使用。说明: 在线代码演示中注释引入
<!-- directive: my-dir -->
没有成功引入指令内容,这是由于在线codepen不支持,放到自己IDE中是没问题的。参数2:scope
scope是指令的作用域,每个指令创建后可以继承父作用域(外部
controller
提供的作用域或根作用域$rootScope
),也可以拥有自己独立的作用域,scope
参数有三种,分别是:false、true、{}
,默认为false
。scope = false
当
scope
设置为false
时,指令模板中可以直接使用父作用域中得变量和函数,举个例子:Html代码:
Js代码:
在线代码演示
从在线代码演示效果中我们看到创建的指令继承了父作用域
controller
中的属性name、age
,同样也继承了方法changeAge
。当改变指令模块中input
的name
会发现父作用域controller
中的name
也会发生变化,这说明指令的作用域和父作用域在同一个作用域下。scope = true
当
scope
参数改为true
时,我们再来看:在线代码演示
同样指令中继承了父作用域的属性和方法,不同的是,当修改指令
input
的name
时,指令的name
对应改变,父作用域controller
中的name
并未发生改变,这说明指令的作用域和父作用域不在同一个作用域下。scope = {}
当
scope
设置为{}
会使指令的作用域变得更加灵活,修改之前的代码:Html代码:
Js代码:
在线代码演示
我们使用了隔离的作用域,不代表我们不可以使用父作用域的属性和方法,我们可以通过向
scope
的{}
中传入特殊的前缀标识符(即prefix
),来进行数据绑定。我们可以通过前缀标识符
@, &, =
应用到指令中的属性,如:{name: '@myName', age: '=', changeAge: '&changeMyAge'}
(age: '='
是age: '=age'
的简写),我们可以在<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>
这个元素中,通过使用属性my-name,age,change-my-age
来引用这些属性的值。首先可以确定的是
scope={}
不会继承父作用域,因为我的性别项male
值是空的,在scope
中未进行属性male
绑定。那么前缀标识符
@, &, =
分别有什么作用呢?@
这是一个单向数据绑定的前缀标识符。在元素中使用属性,如:
<div my-dir my-name="{{name}}"></div>
,属性的名字要用“-”将两个单词连接,因为是数据的单向绑定所以要通过使用{{}}
来绑定数据。在线代码演示结果中修改input
的name
值可看出,只指令的作用域生效name
发生改变,父作用域name
未改变,这点和scope
参数为true
效果是一样的。=
这是一个双向数据绑定的前缀标识符。在元素中使用属性,如:
<div my-dir age="age"></div>
,因为是数据的双向绑定,所以使用=
前缀实现,而不是{{}}
。在线代码演示结果中修改input
的name
值可看出,指令的作用域和父作用域name
均发生改变,这点和scope
参数为false
效果是一样的。&
这是一个绑定函数方法的前缀标识符。在元素中使用属性,如:
<div my-dir change-my-age="changeAge()"></div>
,属性的名字要用“-”将每个单词连接。在线代码演示结果中点击“修改年龄”按钮,指令作用域和父作用域中的年龄都发生改变,这点和前缀标识符=
效果相同。注意: 在新创建指令的作用域对象中,使用属性的名字进行绑定时,要使用驼峰命名标准,如下面的代码:
进一步说明,我们创建的指令是如何利用这些前缀标识符来寻找我们想要的属性或函数的呢?
使用
@
时当指令编译到模板的name
,就会到scope
中查找是否含有name
的键值对(name:'@myName'
),找到后发现前缀标识符@
就知道这是一个单向数据绑定标识符,之后会去寻找元素上含有这个值的属性(my-name={{name}}
),然后在父作用域中查找{{name}}
的值,得到之后传递给属性my-name
,通过指令参数scope
中键值对name:'@myName'
最后传给了模板中的name
。(此处比较绕,不过真心值得研究一番,有助于深刻理解)。=
和&
与@
差不多,只不过=
进行的是双向的数据绑定,不论模板还是父作用域上的属性的值发生改变都会使另一个值发生改变,而&
是绑定函数而已。总结:
scope = false
,scope = true
初始化都会继承父作用域中的属性和方法,不同的是scope = false
和父作用域在同一作用域下,scope = true
和父作用域不在同一作用域,而是创建了一个独立的作用域。scope = {}
不会继承父作用域,属性控制更加灵活,单向绑定@
,外部scope
能够影响内部scope
,但反过来不成立,双向绑定=
,外部scope
和内部scope
的model
能够相互改变,函数绑定&
把内部scope
的函数和外部scope
的函数绑定起来。参数3:template
template
参数值可以是一个字符串也可以是一个函数,值为字符串比较好理解,上文一直在演示的就是值为字符串的形式。当值为函数时,可接收两个参数element
和attrs
。举例:Html代码:
Js代码:
在线代码演示
函数参数
element
是指使用此指令的元素<my-dir name="echeverra" age="20" title="指令元素"></my-dir>
,而attrs
是指令元素上所有属性的集合,形如:参数4:templateUrl
templateUrl
和上一个参数template
用法基本相同,只不过template
值为字符串是一段html模板,templateUrl
值为字符串是文件路径。如:具体使用请参考参数
template
。参数5:replace
replace
是一个可选参数,默认值为false
,表示指令中模板template
内容会当做子元素插入到指令元素中,如果设置为true
,表示指令中模板会直接替换掉指令元素。举例
replace
为false
:Html代码:
Js代码:
在线代码演示
点击获取引入指令元素按钮后显示
<my-dir><h1>这是自定义指令</h1></my-dir>
,可以看到指令元素<my-dir></my-dir>
还在,如果将replace
设置为true
,我们在来看:在线代码演示
这次获取只显示元素
<h1>这是自定义指令</h1>
,说明指令元素<my-dir></my-dir>
被替换掉了,这就是两者的区别。参数6:priority
当同一个元素声明两个指令,需要设定执行先后顺序,这时就需要用到参数
priority
,值为数字,默认值为0,我们来看下不使用priority
的情况会有什么麻烦。Html代码:
Js代码:
在线代码演示
输出:
Hello Angular World !
,可见先执行了ng-init="greeting='Hello '"
,然后执行了指令d2
,最后是d1
。实际我们想预期输出的是Hello World Angular !
,我们将指令元素中的d1、d2
顺序对调,再将指令中创建的d1、d2
两个指令顺序对调,再试发现输出的还是Hello Angular World !
,这是因为Angular
是使用字母顺序来确定链接函数谁先被调用,将d1
改为e1
就会得到预期的结果,有兴趣的可以尝试一下。如何设定指令执行优先级?这时候priority
就派上用场了。Js代码:
在线代码演示
输出:
Hello World Angular
。参数7:link
link
函数主要用于操作Dom
元素,给Dom
元素绑定事件和监听,link
参数要求声明一个函数,称之为链式函数。写法:
scope
:指令所在的作用域element
:指令元素的封装,可以调用angular封装的简装jq方法和属性attr
:指令元素的属性的集合如果指令使用了require选项,那么链接函数会有第四个参数,代表控制器或者所依赖的指令的控制器。
下面举例操作
Dom
元素,改变元素样式。Html代码:
Js代码:
在线代码演示
点击
code
,获取指令元素的属性name
值,ele
调用text()
展示,同时ele
绑定bind
鼠标悬浮事件,调用css()
方法,改变鼠标样式。ele
是指令元素的jqLite包装,所以可以基本的jq
方法,如例子中的bind()
,css()
等。结语: 还有一些其他不常见的属性,本文没有介绍。如有错误,欢迎指出,感谢以下博文。
https://blog.csdn.net/m0_37885651/article/details/80949613
https://segmentfault.com/a/1190000002773689
https://www.cnblogs.com/sagacite/p/4622092.html
https://segmentfault.com/a/1190000005851663