修真院Web工程师零基础全能课javascript
本节课内容html
AngularJs进阶-指令java
主讲人介绍angularjs
沁修,葡萄藤技术总监app
项目经验丰富,擅长H5移动项目开发。异步
专一技术选型、底层开发、最佳代码实践规范总结与推广。函数
直播录屏版post
传送门:https://v.qq.com/x/page/p0775...性能
文字版解析ui
概述
指令是什么?
其实咱们从一开始接触到angularjs就在使用指令了
指令就像是教给html的一些小花招,它会附加在html的元素上,以自定义标记的方式,告诉angularjs的html编译器:
这某个元素加上了一些特定的行为或者是方法,还能够对html的元素进行各类操做。
angularjs中它自己是内置了一整套指令的,好比ngBind,ngClick,ngApp,ngView等等
但也能够本身建立本身的指令来方便咱们的开发和使用。
咱们先来看一下怎么去使用指令directive?
当angular启动咱们的程序时,html编译器$compile就会遍历整个DOM,而且会去匹配DOM元素里的指令
那这些指令一般是怎么放置在html中的呢?
一般有4种方法:
<!-- 1. 属性的方式 -->
<spanmy-dir="exp"></span>
<!-- 2. 类的方式 -->
<spanclass="my-dir: exp;"></span>
<!-- 3. 元素的方式 -->
<my-dir></my-dir>
<!-- 4. 注释的方式 -->
<!-- directive: my-dir exp -->
不过建议你们以属性的方式为主,元素的方式为辅,其余的两种方式能够不去使用。
这样更容易看出来一个元素是跟那个指令相匹配的,更由于不少的指令都被限制为属性的方式。
这里就很少作演示了,咱们默认都使用属性的方式。
指令的命名规范是?
指令有许多,如ngView, ngBind, ngModel等等。
但写在html中的时候有个问题就是html是不识别大小写区别的,所以驼峰的写法就会有问题。
有了指令匹配到元素上后,咱们的编译器还须要经过另外的命名规范来匹配它们是不是一个指令:
所以命名规范会将驼峰式改成全小写,而后用破折号或者其余符号间隔的形式来实现,好比如下写法都是正确合法的:
咱们试着使用一下它:
<divng-controller="Fn1">
<p>{{ user }}</p>
</div>
<divng_controller="Fn2">
<p>{{ user }}</p>
</div>
<divng:controller="Fn3">
<p>{{ user }}</p>
</div>
<divdata-ng-controller="Fn4">
<p>{{ user }}</p>
</div>
<divx-ng-controller="Fn5">
<p>{{ user }}</p>
</div>
<scriptsrc="bower_components/angular/angular.js"></script>
<scripttype="text/javascript">
varmyApp=angular.module("myApp", []);
myApp.controller('Fn1',function($scope) {
$scope.user='Alex';
});
myApp.controller('Fn2',function($scope) {
$scope.user='Bill';
});
myApp.controller('Fn3',function($scope) {
$scope.user='Coco';
});
myApp.controller('Fn4',function($scope) {
$scope.user='Danel';
});
myApp.controller('Fn5',function($scope) {
$scope.user='Eleven';
});
接下来看看指令在html中的编译步骤:
1.首先,将html转换为DOM对象
2.而后它会遍历整个DOM,识别指令,将指令和与之对应DOM一块儿放到directive列表当中。
按顺序执行他们每个的compile()函数(编译函数),这个时候他们会拥有一个修改DOM结构的机会,而且会产生一个link()函数。
所以最后会返回一个组合的linking函数,是页面中全部指令自身的compile函数返回的linking函数集合。
3.经过上一步返回的linking函数集合,把html模板和scope做用域链接起来,同时还能注册一些监听器。
最后让scope和DOM之间造成双向即时的绑定。
咱们来看一个例子:
<inputtype="text"ng-model="userName"><buttonng-click="addUser()">add</button>
<ul>
<ling-repeat="user in users">{{user}}</li>
</ul>
经过这个例子,咱们来理解编译过程为啥要分为compile和link两步。
就是由于有时候须要在model发生改变后,对应DOM结构也要跟着改变。
其中ng-model就是一个指令,ng-repeat又是另一个。
但ng-repeat有一个问题就是它须要很快的根据model制造出多个新的li,li元素须要被克隆和插入到ul当中。
并且仅仅只是克隆是不够的,还需的要编译,以便它的{{user}}能够被正确的scope解析。
若是咱们只是简单的拷贝插入一个li元素,而后编译,这样每个li元素发生改变,都会遍历整个DOM树从新运行。
若是的repeat的item有成百上千个,就会有性能问题。
因此解决方案就是把编译分为两个步骤:
compile阶段识别全部的directive,而后按照优先级排序,该改变DOM就改变DOM。
到linking阶段再将scope和li绑定在一块儿。
自定义指令?
咱们先写一个最简单的指令:
好比有这么一段带有我的信息的模板,会在代码中重复屡次,那么咱们就可使用指令来简化这个过程。
固然指令和控制器同样,也是注册在模块上的,咱们先建立一个指令,来替换本来的内容:
<divng-controller="DirCtrl1">
<divppt-time></div>
</div>
varmyApp=angular.module("myApp", []);
myApp.controller('DirCtrl',function($scope) {
$scope.user='Tom';
$scope.current=newDate();
$scope.task=2;
})
myApp.directive('pptTime',function() {
return{
template:'{{user}}领取任务{{task}}的时间点为:{{current}}'
}
})
这里能够看到,控制器DirCtrl里的变量,能够被指令使用并展现在视图上。
咱们来看看发生了什么事:
1.首先将HTML转换为DOM对象。
2.对DOM对象进行编译,遍历DOM而且对指令进行匹配。
匹配上的指令会有个修改DOM结构的机会,而且产生一个link函数并返回link函数集合
3.经过返回的link函数集合,把模板和scope链接起来。
这样在scope和DOM之间会有一个双向即时的绑定,当scope发生变化的时候,DOM也会发生对应的改变。
这样咱们就有基本的指令可使用了,但这样有一点很差就是这不是一个好的代码实践:
咱们但愿将模板分割出来,让视图和代码分离,所以能够将template这个选项改成templateUrl来加载相应的html模板文件,会优雅许多。
自定义指令的模板
angular.module('app', []).directive('myDirective',function() {
return{
restrict:String,
priority:Number,
terminal:Boolean,
template:String,
templateUrl:String,
replace:BooleanorString,
transclude:Boolean,
scope:BooleanorObject,
controller:Stringorfunction(scope, element, attrs, transclude, otherInjectables) { },
controllerAs:String,
require:String,
link:function(scope, iElement, iAttrs) { },
compile:function(tElement, tAttrs, transclude) {
return{
pre:function(scope, iElement, iAttrs, controller) { },
post:function(scope, iElement, iAttrs, controller) { }
}
returnfunctionpostLink() { }
}
}
})
咱们来看一下这个自定义的指令的参数,首先restrict的值是一个string,做用是申明在模板中如何使用,它有四个值:
E,A,C,M
一般咱们都推荐使用元素和属性的方式
E元素<ptt-time></ptt-time>A属性(默认)<div ptt-time></div>C样式类<div class=“ptt-time”></div>M注释<!— directive: ptt-time -->
priority: number表示的是指令的执行优先级,若是在单个DOM上有多个指令,则优先执行优先级高的。这个指令不多使用
template,是指令连接的DOM模板
templeteUrl,是指定一个字符串形式的内嵌模板,加载的模板是经过异步加载的
replace,是指令连接模板是否替换原有的元素
<hello>
<div>这里是指令内部的内容。</div>
</hello>
angular.module("MyModule", []).myModule.directive("hello",function() {
return{
restrict:"AE",
template:"<div>Hello World!</div>",
replace:true
}
});
transclude,和replace有一点类似,但它是让标识符里原有的内容带到新的位置上。
开启这个以后,就能够在指令的模板中使用ng-transclude来指明什么地方放内容。
<hello>
<div>这里是指令内部的内容。</div>
</hello>
angular.module("MyModule", []).myModule.directive("hello",function() {
return{
restrict:"AE",
transclude:true,
template:"<div>Hello everyone!<div ng-transclude>你看不见我</div></div>"
}
});
link,这是为目标DOM元素添加事件监听,创建数据绑定。
它的scope参数是表示与指令元素相关的做用域,element是当前指令所对应的元素,attrs是当前元素的属性所组成的对象
controller,是船舰一个控制器,经过标识符来公开通讯API,而且给指令暴露出一组方法,供外部调用。
它的参数scope是和指令相关联的做用域:
element,是当前指令对应的元素
attrs,是当前元素的属性组成的对象
transclude,是嵌入连接函数
其实指令的controller和link函数能够互换,它们的区别在于:
控制器主要提供能够在指令之间复用的行为,但link是只能在当前内部指令中执行的行为且没法在指令之间复用。
关于controller和require的用法范例
require,它的值表明了另外个指令的名字,而且会做为link的第四个参数。
假设如今咱们要编写三个指令,三个指令中的link连接函数中存在有不少重合的方法。
这时候咱们就能够将这些重复的方法写在一个指令的controller中。
而后在这三个指令中:
require这个拥有controller字段的的指令,最后经过link连接函数的第四个参数就能够引用这些重合的方法了。
<superman>super</superman>
<superman speed>super and speed </superman>
varmyModule=angular.module("MyModule", []);
myModule.directive("superman",function() {
return{
scope: {},
restrict:'AE',
controller:function($scope) { // 定义了三个公共方法,供外部指令访问
$scope.abilities=[];
this.addStrength=function() {
$scope.abilities.push("strength");
};
this.addSpeed=function() {
scope.abilities.push("speed");
};
this.addLight=function() {
$scope.abilities.push("light");
};
},
link:function(scope, element, attrs) {
element.bind("mouseenter",function() {
console.log(scope.abilities);
});
}
}
});
myModule.directive("speed",function() {
return{
require:'^superman',
link:function(scope, element, attrs, supermanCtrl) {
supermanCtrl.addSpeed();
}
}
});
最后讲一下scope,它的做用是建立一个新的做用域。
它的值有3种:
false,true,{}
当它等于false的时候,咱们在哪一个controller中使用这个指令,它就会继承这个ctrl的值:
也就是说它们之间不会隔离,哪一边的值发生变化,另一边也会跟着变:
myApp.directive('hello',function() {
return{
restrict:'AE',
scope:false,
template:'<div><input type="text" ng-model="userName"/> {{ userName }} </div>',
replace:true
}
})
当它等于true的时候,指令就会继承控制器的值,改变控制器里的值,指令里面的值也会随之变化。
可是改变指令的值,控制器的却不会变,这就是产生了继承隔离。
当它等于{}时,就不会继承任何值,彻底产生隔离。
而后,给你们布置一个做业,关于scope里的属性绑定策略,有:
@attr单向文本绑定
=attr双向绑定
&调用符做用域中的函数
本身去了解它们是怎么使用的。
以上就是上节课的内容解析啦