大家好,我是IT修真院武汉分院web第17期的学员吴三水,一枚正直纯洁善良的web程序员。
今天给大家分享一下,修真院官网js(职业)任务八,深度思考中的知识点——angular js自定义指令 directive 如何使用?为什么要使用封装的自定义指令?
1.背景介绍
什么是指令
指令就是DOM与逻辑行为的媒介,本质就是DOM绑定的独立逻辑行为函数。
angular通过内置指令的方式实现了对html的拓展,同时也赋予了我们自定义指令的功能,让我们根据实际需求进行指令的定制。自定义指令涉及到模板(template)、作用域(scope)、数据绑定和Dom操作等内容。
2.知识剖析
如何自定义指令
我们可以用directive 函数来添加自定义的指令。
基础语法
App.directive("directiveName",function(){
return {
//指令可用在何处,一般有E(Elemenmt)、A(Attribute)、C(Class),还有一个注释,这个就算了吧。默认为A,也就是作为属性用。
restrict: 'E',
//是否替换指令元素。一般这样调用元素<dialog></dialog>,当为true的时候,页面就不会出现dialog元素。
replace: true,
//指令模板,一般模板复杂的话,可以单独放置一个文件,然后使用templateUrl进行引用。
template: '<div>。。。</div>',
//这儿是作用域,如果没有这个配置,则公用所在controller的域;如果有scope,则起到了隔离作用,这儿的key是template的表达式,value指的是元素的属性key。
scope: {
key:value
}
};
});
属性详解
directive( )模块解析(2个参数):
-
name (字符串)
指令的名字,用来在视图中引用特定的指令。 -
factory_function (函数)
这个函数返回一个对象,其中定义了指令的全部行为。
$compile 服务利用这个方法返回的对象,在DOM调用指令时来构造指令的行为。当AngularJS启动应用时,它会把第一个参数当作一个字符串,并以此字符串为名来注册第二个参数返回的对象。AngularJS编译器会解析主HTML的DOM中的元素、属性、注释和CSS类名中使用了这个名字的地方,并在这些地方引用对应的指令。当它找到某个已知的指令时,就会在页面中插入指令所对应的DOM元素。
restrict(字符串|可选):
1.E(Elemenmt):<runoob-directive></runoob-directive>
2.A(Attribute): <div runoob-directive></div>
3.C(Class):<div class="runoob-directive"></div>
4.M(comment): <!-- directive: runoob-directive -->
template和templateUrl(字符串或函数|可选)
这两个只能同时存在其中一个。
分别为:html文本的字符串获取html文本链接的字符串;
如果也函数的时候则为:
一个可以接受两个参数的函数,参数为 tElement 和 tAttrs ,并返回一个代表模板的字符串;
一个可以接受两个参数的函数,参数为 tElement 和 tAttrs ,并返回一个外部HTML文件路径的字符串。
replace和transclude(布尔值|可选)
replace:是否用模板替换当前元素。
true : 将指令标签替换成temple中定义的内容,页面上不会再有标签;
false :则append(追加)在当前元素上,即模板的内容包在标签内部。默认false。
transculde:是否使用ng-transculde来包含html中指令包含的原有的内容(不会被替换),默认false。
scope(布尔值|对象|可选)
scope:指令是被赋予允许访问父DOM元素对应作用域的能力.默认false;
当该值为false时,将会直接访问父元素的作用域,此时指令中对作用域中元素的修改也会直接作用在父元素的作用域中。 当该值为true时,则会继承父元素作用域,并创建一个新的作用域对象。
如果为一个对象的时候,他会隔绝作用域。如空对象{},此时指令的作用域将会完全独立于外界。
controller (字符串或函数)
若该属性的值是字符串,则会以该字符串值去应用中查找该控制器。
controllerAs (字符串)
用于设置控制器的别名
compile (对象或函数)
angular应用在启动后会经历两个阶段,一个是编译,一个是链接。 编译阶段会遍历整个HTML文档,编译各个指令和模板,一旦编译阶段完成,便会调用编译函数,编译函数的参数包含有访问指令声明所在的元素(tElemente)及该元素其他属性(tAttrs)的方法。
link(函数):
link:function(scope, element, attrs){
//在这里操作DOM
}
- scope:未直接指明scope作用域时,此scope共享controller的scope。
- element:指标记了该指令的且由angular封装后的元素,封装了基本的方法,跟jQuery封装的用法差不多。
- attrs:标记了该指令的元素的所有属性封装的对象。比如本例子中,标记了hello指令的button带有class属性,可以使用attrs.class 取出class属性的值。
如果指令使用了require选项,那么链接函数会有第四个参数,代表控制器或者所依赖的指令的控制器。
// require 'meController'
link: function(scope, element, attrs, meController){
//在这里操作DOM, 可以访问requires指定的控制器
}
常见问题
如何使用自定义指令
解决方案
<body>
<my-hello></my-hello>
</body>
<script type="text/javascript">
var m1 = angular.module('myApp',[]);
m1.directive('myHello',function(){
return {
restrict : 'E',
replace : true,
template : '<div>hello angular</div>'
};
});
</script>
编码实战
对上述代码进行拓展
<body>
<my-hello>这是一个栗子</my-hello>
<div my-hello>这是一个栗子</div>
<div class="my-hello">这是一个栗子</div>
<!-- directive:my-hello -->`
</body>
<script type="text/javascript">
var m1 = angular.module('myApp',[]);
m1.directive('myHello',function(){
return {
restrict : 'EACM',
replace : true,
template : '<div>hello angular</div>'
};
});
//templateUrl 挂载内容模板的路劲地址
// transculde: true 内容会被收进原来的内容会被放在有ng-transclude属性的标签内
</script>
<body ng-app="main">
<div ng-controller="indexController">
<p ng-bind="count"></p>
<button class="btn btn-primary" hello>count</button>
</div>
<script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module("main", []);
app.controller("indexController", ['$scope', function($scope) {
$scope.count=1;
}]);
app.directive('hello',function(){
return {
restrict:'A',
link:function(scope,element,attrs){
element.on('click',function(){
scope.$apply(function(){
scope.count++;
});
});
}
};
});
</script>
</body>
<body ng-app="main">
<div ng-controller="indexController">
<p>{{username}}</p>
<p hello>
{{username}}
</p>
</div>
<script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module("main", []);
app.controller("indexController", ['$scope', function($scope) {
}]);
app.directive('hello',function(){
return {
scope:true, /*或者 scope:{} 都表示指令声明了自己独立的scope*/
restrict:'A',
controller:function($scope){
$scope.username='Mike';
}
};
});
</script>
</body>
还是hello指令,不同的是在自定义指令中返回的对象中设置了scope:true,表示声明指令的scope是私有的,并为指令创建了独立的控制器,为私有scope赋值username属性为Mike,在页面上在hello指令下取 username 是可以取到的,但是在hello指令外取的username是没有值的。
<body ng-app="main">
<div ng-controller="indexController">
<p>{{username}}</p>
indexController下的scope值 <input type="text" ng-model="username" />
<p hello name="{{username}}">
指令私有scope: {{name}}
</p>
</div>
<script type="text/javascript" src="../js/jquery1.12.4.min.js"></script>
<script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module("main", []);
app.controller("indexController", ['$scope', function($scope) {
$scope.username="Lily";
}]);
app.directive('hello',function(){
return {
scope:{
name:'@'
},
restrict:'A',
relplace:true,
template:'<p><p>指令私有scope中的值:{{name}}<p><button class="btn btn-primary">change private</button></p>',
link:function(scope,ele,attrs){
$(ele).find("button").on("click",function(){
scope.$apply(function(){
scope.name="Mike";
});
});
}
};
});
</script>
</body>
解析:将indexController的scope中的username的值传递给了自定义指令hello,input框绑定的是indexController的scope中的模型,指令我们为指令模板中的button添加点击事件,当点击按钮时,改变私有scope中name的值。
运行结果:当我们在input框中改变可以看到指令所有scope中的值也会响应变化,因为@绑定策略就是将模型的值传递给指令,字符串传递。但是当我们点击按钮改变私有scope的name的值的时候,indexController的scope是不会响应这种变化的。
=,将上面demo的@改为=
:‘=’绑定策略传递的是对象
运行结果:两个scope中模型的值是双向绑定的,改变任何一个,另一个也会响应这种变化。
&:函数传递,指令调用
<body ng-app="main">
<div ng-controller="indexController">
<p hello say="sayEnglish()"></p>
</div>
<script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module("main", []);
app.controller("indexController", ['$scope', function($scope) {
$scope.sayEnglish=function(){
alert('Good Morning!');
};
}]);
app.directive('hello',function(){
return {
scope:{
say:'&'
},
restrict:'A',
relplace:true,
template:'<p><button class="btn btn-primary" ng-click="say()">打声招呼</button></p>'
};
});
</script>
</body>
解析:将外层indexController的scope中的sayEnglisg()方法传递给了指令,指令内部在调用say() 方法时,实际调用的是 sayEnglish();
6.拓展思考
指令就相当于为我们写了公共的自定义DOM元素或CLASS属性或ATTR属性,并且它不只是单单如此,你还可以在它的基础上来操作scope、绑定事件、更改样式等。通过这个Directive,我们可以封装很多公共指令,比如分页指令、自动补全指令等等。然后在HTML页面里只需要简单的写一行代码就可以实现很多强大的功能。
7.参考文献
8.更多讨论
1.什么时候用指令?
- 使你的Html更具语义化,不需要深入研究代码和逻辑即可知道页面的大致逻辑。
- 抽象一个自定义组件,在其他地方进行重用。
2.为什么不推荐Angular和JQuery混合使用?
在Angular中,操纵数据不是通过抓取和注入。一般通过数据绑定来实现,JQurey是抓取dom节后进行操作的。核心理念不同。 虽然在Angular里也能通过抓取然后注入数据,但是会出现很多不必要的问题和bug.
3.为什么在directive里尽量不要用大写。
因为在directive里,会将遇到的大写字母转换成 “-” + 转换的小写字母。
感谢大家观看!
今天的分享就到这里啦,欢迎大家点赞、转发、留言、拍砖~