前面提到了angularjs的factory,service,provider,这个能够理解成php的model,这种model是不带html的,今天所说的directive,也能够理解成php的model,也能够理解成插件,只不过这种model是带html的,例如:php的分页函数。express
一,angularjs directive的经常使用格式,以及参数说明数组
1,直接returnapp
- var phonecatDirectives = angular.module('phonecatDirectives', []);
- phonecatDirectives.directive('directiveName', function($inject) {
- return {
- template: '<div></div>',
- replace: false,
- transclude: true,
- restrict: 'E',
- scope: { ... },
- controller: function($scope, $element){ .... },
- compile: function(tElement, tAttrs, transclude) {
- return {
- pre: function preLink(scope, iElement, iAttrs, controller) { ... },
- post: function postLink(scope, iElement, iAttrs, controller) { ... }
- }
- },
- link: function(scope, iElement, iAttrs) { ... }
- };
- });
2,定义一个js的域异步
- var phonecatDirectives = angular.module('phonecatDirectives', []);
- phonecatDirectives.directive('directiveName', function($inject) {
- var mydi = {
- template: '<div></div>',
- ..................... ,
- link: function(scope, iElement, iAttrs) { ... }
- };
- return mydi;
- });
3,angularjs directive 对像参数说明ide
name - 当前scope的名称,注册时可使用默认值(不填)。函数
priority(优先级)- 当有多个directive定义在同一个DOM元素时,有时须要明确它们的执行顺序。这属性用于在directive的compile function调用以前进行排序。若是优先级相同,则执行顺序是不肯定的(经初步试验,优先级高的先执行,同级时按照相似栈的“后绑定先执行”。另外,测试时有点不当心,在定义directive的时候,两次定义了一个相同名称的directive,但执行结果发现,两个compile或者link function都会执行)。
terminal(最后一组)- 若是设置为”true”,则表示当前的priority将会成为最后一组执行的directive。任何directive与当前的优先级相同的话,他们依然会执行,但顺序是不肯定的(虽然顺序不肯定,但基本上与priority的顺序一致。当前优先级执行完毕后,更低优先级的将不会再执行)。
scope - 若是设置为:
true - 将为这个directive建立一个新的scope。若是在同一个元素中有多个directive须要新的scope的话,它仍是只会建立一个scope。新的做用域规则不适用于根模版(root of the template),所以根模版每每会得到一个新的scope。
{}(object hash) - 将建立一个新的、独立(isolate)的scope。”isolate” scope与通常的scope的区别在于它不是经过原型继承于父scope的。这对于建立可复用的组件是颇有帮助的,能够有效防止读取或者修改父级scope的数据。这个独立的scope会建立一个拥有一组来源于父scope的本地scope属性(local scope properties)的object hash。这些local properties对于为模版建立值的别名颇有帮助(useful for aliasing values for templates -_-!)。本地的定义是对其来源的一组本地scope property的hash映射(Locals definition is a hash of local scope property to its source #&)$&@#)($&@#_):
@或@attr - 创建一个local scope property到DOM属性的绑定。由于属性值老是String类型,因此这个值老是返回一个字符串。若是没有经过@attr指定属性名称,那么本地名称将与DOM属性的名称一直。例如<widget my-attr=”hello {{name}}”>,widget的scope定义为:{localName:’@myAttr’}。那么,widget scope property的localName会映射出”hello {{name}}"转换后的真实值。name属性值改变后,widget scope的localName属性也会相应地改变(仅仅单向,与下面的”=”不一样)。name属性是在父scope读取的(不是组件scope)
=或=expression(这里也许是attr) - 在本地scope属性与parent scope属性之间设置双向的绑定。若是没有指定attr名称,那么本地名称将与属性名称一致。例如<widget my-attr=”parentModel”>,widget定义的scope为:{localModel:’=myAttr’},那么widget scope property “localName”将会映射父scope的“parentModel”。若是parentModel发生任何改变,localModel也会发生改变,反之亦然。(双向绑定)
&或&attr - 提供一个在父scope上下文中执行一个表达式的途径。若是没有指定attr的名称,那么local name将与属性名称一致。例如<widget my-attr=”count = count + value”>,widget的scope定义为:{localFn:’increment()’},那么isolate scope property “localFn”会指向一个包裹着increment()表达式的function。通常来讲,咱们但愿经过一个表达式,将数据从isolate scope传到parent scope中。这能够经过传送一个本地变量键值的映射到表达式的wrapper函数中来完成。例如,若是表达式是increment(amount),那么咱们能够经过localFn({amount:22})的方式调用localFn以指定amount的值(上面的例子真的没看懂,&跑哪去了?)。
controller - controller 构造函数。controller会在pre-linking步骤以前进行初始化,并容许其余directive经过指定名称的require进行共享(看下面的require属性)。这将容许directive之间相互沟通,加强相互之间的行为。controller默认注入了如下本地对象:
$scope - 与当前元素结合的scope
$element - 当前的元素
$attrs - 当前元素的属性对象
$transclude - 一个预先绑定到当前转置scope的转置linking function :function(cloneLinkingFn)。(A transclude linking function pre-bound to the correct transclusion scope)
require - 请求另外的controller,传入当前directive的linking function中。require须要传入一个directive controller的名称。若是找不到这个名称对应的controller,那么将会抛出一个error。名称能够加入如下前缀:
? - 不要抛出异常。这使这个依赖变为一个可选项。
^ - 容许查找父元素的controller
restrict - EACM的子集的字符串,它限制directive为指定的声明方式。若是省略的话,directive将仅仅容许经过属性声明:
E - 元素名称: <my-directive></my-directive>
A - 属性名: <div my-directive=”exp”></div>
C - class名: <div class=”my-directive:exp;”></div>
M - 注释 : <!-- directive: my-directive exp -->
template - 若是replace 为true,则将模版内容替换当前的HTML元素,并将原来元素的属性、class一并迁移;若是为false,则将模版元素看成当前元素的子元素处理。想了解更多的话,请查看“Creating Widgets”章节(在哪啊。。。Creating Components就有。。。)
templateUrl - 与template基本一致,但模版经过指定的url进行加载。由于模版加载是异步的,因此compilation、linking都会暂停,等待加载完毕后再执行。
replace - 若是设置为true,那么模版将会替换当前元素,而不是做为子元素添加到当前元素中。(注:为true时,模版必须有一个根节点)
transclude - 编译元素的内容,使它可以被directive所用。须要(在模版中)配合ngTransclude使用(引用)。transclusion的优势是linking function可以获得一个预先与当前scope绑定的transclusion function。通常地,创建一个widget,建立isolate scope,transclusion不是子级的,而是isolate scope的兄弟。这将使得widget拥有私有的状态,transclusion会被绑定到父级(pre-isolate)scope中。(上面那段话没看懂。但实际实验中,若是经过<any my-directive>{{name}}</any my-directive>调用myDirective,而transclude设置为true或者字符串且template中包含<sometag ng-transclude>的时候,将会将{{name}}的编译结果插入到sometag的内容中。若是any的内容没有被标签包裹,那么结果sometag中将会多了一个span。若是原本有其余东西包裹的话,将维持原状。但若是transclude设置为’element’的话,any的总体内容会出如今sometag中,且被p包裹)
true - 转换这个directive的内容。(这个感受上,是直接将内容编译后搬入指定地方)
‘element’ - 转换整个元素,包括其余优先级较低的directive。(像将总体内容编译后,看成一个总体(外面再包裹p),插入到指定地方)
compile - 这里是compile function,将在下面实例详细讲解
link - 这里是link function ,将在下面实例详细讲解。这个属性仅仅是在compile属性没有定义的状况下使用。
三,angularjs directive 实例讲解
下面的实例都围绕着,上面的参数来展开的
1,directive声明方式实例
- //directive文件directives.js中定义一个myTest
- 'use strict';
- var phonecatDirectives = angular.module('phonecatDirectives', []);
- phonecatDirectives.directive('myTest', function() {
- return {
- restrict: 'ACEM',
- require: '^ngModel',
- scope: {
- ngModel: '='
- },
- template: '<div><h4>Weather for {{ngModel}}</h4></div>'
- }
- });
- //controller文件controller.js中定义directive1
- 'use strict';
- var dtControllers = angular.module('dtControllers', []);
- dtControllers.controller('directive1',['$scope',
- function($scope) {
- $scope.name = 'this is tank test';
- }
- ]);
- //在app文件app.js中整合controller,directive
- 'use strict';
- var phonecatApp = angular.module('phonecatApp', [
- 'phonecatDirectives',
- 'dtControllers'
- ]);
- //html文件
- <script src="../lib/angular/angular.js"></script>
- <script src="../js/app.js"></script>
- <script src="../js/controller.js"></script>
- <script src="../js/directives.js"></script>
- <body ng-app="phonecatApp">
- <div ng-controller="directive1">
- <input type="text" ng-model="city" placeholder="Enter a city" />
- <my-test ng-model="city" ></my-test>
- <span my-test="exp" ng-model="city"></span>
- <!-- directive: my-test exp -->
- <span ng-model="city"></span>
- </div>
- </body>
上例结果:<!-- directive: my-test exp -->这个不起做用,不知道为何,尝试了好多方法都不起做用。
2,template和templateUrl区别与联系
templateUrl其实根template功能是同样的,只不过templateUrl加载一个html文件,上例中,咱们也能发现问题,template后面根的是html的标签,若是标签不少呢,那就比较不爽了。能够将上例中的,template改一下。
- phonecatDirectives.directive('myTest', function() {
- return {
- restrict: 'ACEM',
- require: '^ngModel',
- scope: {
- ngModel: '='
- },
- templateUrl:'../partials/tem1.html' //tem1.html中的内容就是上例中template的内容。
- }
- });
3,scope重定义
- //directives.js中定义myAttr
- phonecatDirectives.directive('myAttr', function() {
- return {
- restrict: 'E',
- scope: {
- customerInfo: '=info'
- },
- template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
- 'Name: {{vojta.name}} Address: {{vojta.address}}'
- };
- });
- //controller.js中定义attrtest
- dtControllers.controller('attrtest',['$scope',
- function($scope) {
- $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
- $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
- }
- ]);
- //html中
- <body ng-app="phonecatApp">
- <div ng-controller="attrtest">
- <my-attr info="naomi"></my-attr>
- </div>
- </body>
运行结果:
- Name: Naomi Address: 1600 Amphitheatre //有值,由于customerInfo定义过的
- Name: Address: //没值 ,由于scope重定义后,vojta是没有定义的
可能把上面的directive简单改一下,
- phonecatDirectives.directive('myAttr', function() {
- return {
- restrict: 'E',
- template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
- 'Name: {{vojta.name}} Address: {{vojta.address}}'
- };
- });
运行结果以下:根上面正好相反
- Name: Address:
- Name: Vojta Address: 3456 Somewhere Else
4,transclude的使用
transclude的用法,有点像jquery里面的$().html()功能
- //directives.js增长myEvent
- phonecatDirectives.directive('myEvent', function() {
- return {
- restrict: 'E',
- transclude: true,
- scope: {
- 'close': '&onClick' //根html中的on-click="hideDialog()"有关联关系
- },
- templateUrl: '../partials/event_part.html'
- };
- });
- //controller.js增长eventtest
- dtControllers.controller('eventtest',['$scope','$timeout',
- function($scope, $timeout) {
- $scope.name = 'Tobias';
- $scope.hideDialog = function () {
- $scope.dialogIsHidden = true;
- $timeout(function () {
- $scope.dialogIsHidden = false;
- }, 2000);
- };
- }
- ]);
- //event.html
- <body ng-app="phonecatApp">
- <div ng-controller="eventtest">
- <my-event ng-hide="dialogIsHidden" on-click="hideDialog()">
- Check out the contents, {{name}}!
- </my-event>
- </div>
- </body>
- //event_part.html
- <div>
- <a href ng-click="close()">×</a>
- <div ng-transclude></div>
- </div>
5,controller,link,compile有什么不一样
- //directives.js增长exampleDirective
- phonecatDirectives.directive('exampleDirective', function() {
- return {
- restrict: 'E',
- template: '<p>Hello {{number}}!</p>',
- controller: function($scope, $element){
- $scope.number = $scope.number + "22222 ";
- },
- link: function(scope, el, attr) {
- scope.number = scope.number + "33333 ";
- },
- compile: function(element, attributes) {
- return {
- pre: function preLink(scope, element, attributes) {
- scope.number = scope.number + "44444 ";
- },
- post: function postLink(scope, element, attributes) {
- scope.number = scope.number + "55555 ";
- }
- };
- }
- }
- });
- //controller.js添加
- dtControllers.controller('directive2',['$scope',
- function($scope) {
- $scope.number = '1111 ';
- }
- ]);
- //html
- <body ng-app="phonecatApp">
- <div ng-controller="directive2">
- <example-directive></example-directive>
- </div>
- </body>
运行结果:
- Hello 1111 22222 44444 55555 !
由结果能够看出来,controller先运行,compile后运行,link不运行。
将上例中的compile注释掉
- // compile: function(element, attributes) {
- // return {
- // pre: function preLink(scope, element, attributes) {
- // scope.number = scope.number + "44444 ";
- // },
- // post: function postLink(scope, element, attributes) {
- // scope.number = scope.number + "55555 ";
- // }
- // };
- // }
运行结果:
- Hello 1111 22222 33333 !
由结果能够看出来,controller先运行,link后运行,link和compile不兼容。
scope:{}
使指令与外界隔离开来,使其模板(template)处于non-inheriting(无继承)的状态,固然除非你在其中使用了transclude嵌入,这点以后的笔记会再详细记录的。可是这显然不符合实际开发中的需求,由于实际上,咱们常常想要咱们的指令可以在特定的状况下与外界进行数据上的交互,这就须要借助绑定策略之手了。
你们知道,当scope选项写为scope:{}这种形式的时候,就已经为指令生成了隔离做用域,如今,咱们来看看绑定策略的
三种形式:
& 绑定方法,将传入的方法名在指令里注册为方法
= 绑定对象.双向绑定
@ 绑定字符串. 设置Title之类的初始化字符. 单向绑定
首先是@,它将本地做用域和DOM中的属性值绑定起来(且这个属性的值必须是父级做用域中的),什么意思呢?说的简单一点就是假设你在模板中有个双花括号表达式,而后咱们把表达式里的内容和html中指令里特定名字的属性绑定起来,仍是不懂?看看下面的代码:
JS代码:
directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:{{ name }}</div>',
scope:{
name:'@forName'
}
}
})
.controller("nameController",function($scope){
$scope.Name="张三";
});
HTML代码:
<div ng-controller="nameController">
<direct for-name="{{ Name }}"></direct>
<div>
运行结果可想而知,{{ name }}成功地与父控制器中的Name绑定起来了。固然这里也能够这样写
name:'@' 这样写的话,就默认DOM中的属性名为name了意即 for-name="{{ Name }}"可简写为name="{{ Name }}";其实,另外两个符号=和&也有这样的简写规则,方便起见接下来都使用这种写法。
@到此为止,接下来就是'='了。=与@的不一样点在于,@是针对字符串而用,但=是针对某个对象的引用,
这么说可能不太专业,但就拿上边的例子而言,咱们在html中,把Name这个字符串经过一对双花括号传递给for-name属性,但若是咱们用了=,这里传入的Name就不该该是一个字符串,而是一个对象的引用。这不是一个很一目了然的概念,因此我用接下来的两个例子诠释它的含义。
第一个例子:数组中的对象的引用
JS代码:
directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:{{ case.name }}</div>',
scope:{
case:'='
}
}
})
.controller("nameController",function($scope){
$scope.data=[{name:"张三"},{name:"李四"}];
});
HTML代码:
<div ng-controller="nameController">
<direct case="data[0]"></direct>
<direct case="data[1]"></direct>
<div>
结果就是,一个张三,一个李四。这个例子中,data是一个对象数组,里面包含了两个对象,因此,咱们分别把两个对象传递给了case这个属性,case属性就把这个对象的引用传递给了模板中咱们写的{{ case.name }}中的case;而若是你在=后边加上了本身定义的名字,那只要把html里case属性换成那个名字就能够了。
第二个例子:经典的双向输入框
按照Angular的入门案例,建立两个双向绑定的输入框,最简单的实现方式就是:
<input ng-model="test"/>
<input ng-model="test"/>
使用ng-model指令就能够作到了。接着,咱们在本身的指令中实现这个效果。
JS代码:
directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:<input ng-model="model"/></div>',
scope:{
model:'='
}
}
})
.controller("nameController",function($scope){
$scope.data=[{name:"张三"},{name:"李四"}];
});
HTML代码:
<div ng-controller="nameController">
父级scope中:<input ng-model="mark"/>
<direct model="mark"/></direct>
</div>
这就完成了,其实只不过是加了一点小把戏,把ng-model换成了model而已。
注意到,这两个例子中,都是使用对象的引用,而不是单纯的字符串,这也是=能够进行双向绑定的关键。
最后是&符号。它的含义是:对父级做用域进行绑定,并将其中的属性包装成一个函数,注意,是属性,意即,任何类型的属性都会被包装成一个函数,好比一个单纯的字符串,或是一个对象数组,或是一个函数方法,若是是字符串、对象数组和无参的函数,那么可想而知,它们都会被包装成一个无参的函数,如果有参的函数方法则反之,而且咱们须要为其传入一个对象。如今,分别针对有参和无参两种状况举例。
无参状况↓
JS代码:
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>{{ title }}</div>'+'<div><ul><li ng-repeat="x in contents">{{ x.text }}< /li></ul></div>',
scope:{
getTitle:'&',
getContent:'&'
},
controller:function($scope){
$scope.title=$scope.getTitle(); //调用无参函数
$scope.contents=$scope.getContent(); //调用无参函数
}
}
})
.controller("nameController",function($scope){
$scope.title="标题";
$scope.contents =[{text:1234},{text:5678}];
});
HTML代码:
<div ng-controller="nameController">
<direct get-title="title" get-content="contents"></direct>
</div>
这个例子有几个注意点:
1.指令的本地属性(即模板里花括号中的属性)须要从本地取值,因此使用了controller选项,而在controller选项中,两个无参方法分别返回了父级scope中的title字符串和contents对象数组。
2.在HTML中,咱们把设置了get-title和get-content的属性值为title和contents,这实际上就完成了与父级scope的绑定,由于咱们才能够从那儿取得实质的内容。
OK,有参状况↓
JS代码:
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div><input ng-model="model"/></div>'+'<div><button ng-click="show({name:model})">show</button>',
scope:{
show:'&'
}
}
})
.controller("nameController",function($scope){
$scope.showName=function(name){
alert(name);
}
});
HTML代码:
<div ng-controller="nameController">
<direct show="showName(name)"></direct>
</div>
这个例子中,经过模板中的ng-click触发了show函数并将一个叫作model的对象做为name参数传递了进去,而在html中,咱们把show的属性值设为showName(name)。这其中的道理跟无参的例子是大同小异的。
大功告成,@,=,&的绑定策略大概就是这样了。有什么须要补充和纠正的,我恳请各位大神向我提出,谢谢!