指令-Directive

restrict:'A'用作设定用那种方式使用指令. 可组合使用如restrict:'AE'
E - 元素名称: <my-directive></my-directive>
A - 属性名: <div my-directive=”exp”></div>
C - class名: <div class=”my-directive:exp;”></div>
M - 注释 : <!-- directive: my-directive exp -->

&  绑定方法,将传入的方法名在指令里注册为方法

=  绑定对象.双向绑定

@ 绑定字符串. 设置Title之类的初始化字符. 单向绑定


   name:'@forName' 左边是指令里的名字, 右边的forName等于引用时的for-name 如一样可以省略不写

replace: true,用作设置是否替换<div my-directive=”exp”>这里的值,以及div本身.</div>
如设置为False,Div中间可放置其他元素.  不过$scope作用域会出现问题.

angularjs directive 实例 详解


前面提到了angularjs的factory,service,provider,这个可以理解成php的model,这种model是不带html的,今天所说的directive,也可以理解成php的model,也可以理解成插件,只不过这种model是带html的,例如:php的分页函数。

一,angularjs directive的常用格式,以及参数说明

1,直接return

  1. var phonecatDirectives = angular.module('phonecatDirectives', []);  
  2.   
  3. phonecatDirectives.directive('directiveName'function($inject) {  
  4.   
  5.    return {  
  6.      template: '<div></div>',  
  7.   
  8.      replace: false,  
  9.   
  10.      transclude: true,  
  11.   
  12.      restrict: 'E',  
  13.   
  14.      scope: { ... },  
  15.   
  16.       controller: function($scope$element){ .... },     
  17.   
  18.       compile: function(tElement, tAttrs, transclude) {  
  19.        return {  
  20.          pre: function preLink(scope, iElement, iAttrs, controller) { ... },  
  21.          post: function postLink(scope, iElement, iAttrs, controller) { ... }  
  22.        }  
  23.      },  
  24.   
  25.      link: function(scope, iElement, iAttrs) { ... }  
  26.    };  
  27. });  

2,定义一个js的域

  1. var phonecatDirectives = angular.module('phonecatDirectives', []);  
  2.   
  3. phonecatDirectives.directive('directiveName'function($inject) {  
  4.   
  5.    var mydi = {  
  6.      template: '<div></div>',  
  7.       ..................... ,  
  8.      link: function(scope, iElement, iAttrs) { ... }  
  9.    };  
  10.   
  11.    return mydi;  
  12. });  

3,angularjs directive 对像参数说明

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声明方式实例

  1. //directive文件directives.js中定义一个myTest  
  2. 'use strict';  
  3.   
  4. var phonecatDirectives = angular.module('phonecatDirectives', []);  
  5.   
  6. phonecatDirectives.directive('myTest'function() {  
  7.     return {  
  8.         restrict: 'ACEM',  
  9.         require'^ngModel',  
  10.         scope: {  
  11.             ngModel: '='  
  12.         },  
  13.         template: '<div><h4>Weather for {{ngModel}}</h4></div>'  
  14.     }  
  15. });  
  16.   
  17. //controller文件controller.js中定义directive1  
  18. 'use strict';  
  19.   
  20. var dtControllers = angular.module('dtControllers', []);  
  21.   
  22. dtControllers.controller('directive1',['$scope',  
  23.     function($scope) {  
  24.         $scope.name = 'this is tank test';  
  25.     }  
  26. ]);  
  27.   
  28. //在app文件app.js中整合controller,directive  
  29.   
  30. 'use strict';  
  31.   
  32. var phonecatApp = angular.module('phonecatApp', [  
  33.     'phonecatDirectives',  
  34.     'dtControllers'  
  35. ]);  
  36.   
  37. //html文件  
  38. <script src="../lib/angular/angular.js"></script>  
  39. <script src="../js/app.js"></script>  
  40. <script src="../js/controller.js"></script>  
  41. <script src="../js/directives.js"></script>  
  42. <body ng-app="phonecatApp">  
  43.     <div ng-controller="directive1">  
  44.         <input type="text" ng-model="city" placeholder="Enter a city" />  
  45.   
  46.         <my-test ng-model="city" ></my-test>  
  47.         <span my-test="exp" ng-model="city"></span>  
  48.         <!-- directive: my-test exp -->  
  49.         <span ng-model="city"></span>  
  50.     </div>  
  51. </body>  

上例结果:<!-- directive: my-test exp -->这个不起作用,不知道为什么,尝试了好多方法都不起作用。

2,template和templateUrl区别与联系

templateUrl其实根template功能是一样的,只不过templateUrl加载一个html文件,上例中,我们也能发现问题,template后面根的是html的标签,如果标签很多呢,那就比较不爽了。可以将上例中的,template改一下。

  1. phonecatDirectives.directive('myTest'function() {  
  2.     return {  
  3.         restrict: 'ACEM',  
  4.         require'^ngModel',  
  5.         scope: {  
  6.             ngModel: '='  
  7.         },  
  8.         templateUrl:'../partials/tem1.html'   //tem1.html中的内容就是上例中template的内容。  
  9.     }  
  10. });  

3,scope重定义

  1. //directives.js中定义myAttr  
  2. phonecatDirectives.directive('myAttr'function() {  
  3.     return {  
  4.         restrict: 'E',  
  5.         scope: {  
  6.             customerInfo: '=info'  
  7.         },  
  8.         template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +  
  9.                   'Name: {{vojta.name}} Address: {{vojta.address}}'  
  10.     };  
  11. });  
  12.   
  13. //controller.js中定义attrtest  
  14. dtControllers.controller('attrtest',['$scope',  
  15.     function($scope) {  
  16.         $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };  
  17.         $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };  
  18.     }  
  19. ]);  
  20.   
  21. //html中  
  22. <body ng-app="phonecatApp">  
  23.     <div ng-controller="attrtest">  
  24.         <my-attr info="naomi"></my-attr>  
  25.     </div>  
  26. </body>  

运行结果:

  1. Name: Naomi Address: 1600 Amphitheatre      //有值,因为customerInfo定义过的  
  2. Name: Address:       //没值 ,因为scope重定义后,vojta是没有定义的  

可能把上面的directive简单改一下,

  1. phonecatDirectives.directive('myAttr'function() {  
  2.     return {  
  3.         restrict: 'E',  
  4.         template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +  
  5.                   'Name: {{vojta.name}} Address: {{vojta.address}}'  
  6.     };  
  7. });  

运行结果如下:根上面正好相反

  1. Name: Address:  
  2. Name: Vojta Address: 3456 Somewhere Else  

4,transclude的使用

transclude的用法,有点像jquery里面的$().html()功能

  1. //directives.js增加myEvent  
  2. phonecatDirectives.directive('myEvent'function() {  
  3.     return {  
  4.         restrict: 'E',  
  5.         transclude: true,  
  6.         scope: {  
  7.             'close''&onClick'     //根html中的on-click="hideDialog()"有关联关系  
  8.         },  
  9.         templateUrl: '../partials/event_part.html'  
  10.     };  
  11. });  
  12.   
  13. //controller.js增加eventtest  
  14. dtControllers.controller('eventtest',['$scope','$timeout',  
  15.     function($scope$timeout) {  
  16.         $scope.name = 'Tobias';  
  17.         $scope.hideDialog = function () {  
  18.             $scope.dialogIsHidden = true;  
  19.             $timeout(function () {  
  20.                 $scope.dialogIsHidden = false;  
  21.             }, 2000);  
  22.         };  
  23.     }  
  24. ]);  
  25.   
  26. //event.html  
  27. <body ng-app="phonecatApp">  
  28.     <div ng-controller="eventtest">  
  29.         <my-event ng-hide="dialogIsHidden" on-click="hideDialog()">  
  30.             Check out the contents, {{name}}!  
  31.         </my-event>  
  32.     </div>  
  33. </body>  
  34.   
  35. //event_part.html  
  36. <div>  
  37.     <a href ng-click="close()">×</a>  
  38.     <div ng-transclude></div>  
  39. </div>  

5,controller,link,compile有什么不同

  1. //directives.js增加exampleDirective  
  2. phonecatDirectives.directive('exampleDirective'function() {  
  3.     return {  
  4.         restrict: 'E',  
  5.         template: '<p>Hello {{number}}!</p>',  
  6.         controller: function($scope$element){  
  7.             $scope.number = $scope.number + "22222 ";  
  8.         },  
  9.         link: function(scope, el, attr) {  
  10.             scope.number = scope.number + "33333 ";  
  11.         },  
  12.         compile: function(element, attributes) {  
  13.             return {  
  14.                 pre: function preLink(scope, element, attributes) {  
  15.                     scope.number = scope.number + "44444 ";  
  16.                 },  
  17.                 post: function postLink(scope, element, attributes) {  
  18.                     scope.number = scope.number + "55555 ";  
  19.                 }  
  20.             };  
  21.         }  
  22.     }  
  23. });  
  24.   
  25. //controller.js添加  
  26. dtControllers.controller('directive2',['$scope',  
  27.     function($scope) {  
  28.         $scope.number = '1111 ';  
  29.     }  
  30. ]);  
  31.   
  32. //html  
  33. <body ng-app="phonecatApp">  
  34.     <div ng-controller="directive2">  
  35.         <example-directive></example-directive>  
  36.     </div>  
  37. </body>  

运行结果:

  1. Hello 1111 22222 44444 55555 !  

由结果可以看出来,controller先运行,compile后运行,link不运行。

将上例中的compile注释掉

  1. //        compile: function(element, attributes) {  
  2. //            return {  
  3. //                pre: function preLink(scope, element, attributes) {  
  4. //                    scope.number = scope.number + "44444 ";  
  5. //                },  
  6. //                post: function postLink(scope, element, attributes) {  
  7. //                    scope.number = scope.number + "55555 ";  
  8. //                }  
  9. //            };  
  10. //        }  

运行结果:

  1. 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)。这其中的道理跟无参的例子是大同小异的。

大功告成,@,=,&的绑定策略大概就是这样了。有什么需要补充和纠正的,我恳请各位大神向我提出,谢谢!


 
 




原文地址:https://www.cnblogs.com/Full--Stack/p/8041239.html