首页 > 代码库 > angularJS中如何写自定义指令

angularJS中如何写自定义指令

指令定义

  • 对于指令,可以把它简单的理解成在特定DOM元素上运行的函数,指令可以扩展这个元素的功能
  • 例如,ng-click可以让一个元素能够监听click事件,并在接收到事件的时候执行angularJS表达式
  • 正是指令使得angularJS这个框架变得强大,并且正如所见,我们可以自己创造新的指令

指令声明方法

angular.module(‘freefedApp‘,[]).directive(name,function(){
      return {
            restrict: String,
            priority: Number,
            terminal: Boolean,
            template: String or Template Function:function(tElement, tAttrs) (...},
            templateUrl: String,
            replace: Boolean or String,
            scope: Boolean or Object,
            transclude: Boolean,
            controller: String orfunction(scope, element, attrs, transclude, otherInjectables) { ... }, 
            controllerAs: String,
            require: String,
            link: function(scope, iElement, iAttrs) { ... },
       };
});

 

指令作用域

DOM中每个指令调用时都可能会:

  • 直接调用相同的作用域对象
  • 从当前作用域对象继承一个新的作用域对象
  • 创建一个同当前作用域相隔离的作用域对象

作用域scope设置(参数是可选的,默认值是false):

  • true 会从父作用域继承并创建一个新的作用域对象
  • false 直接调用相同作用域对象
  • {} 创建具有隔离作用域,具有隔离作用域的指令最主要的使用场景是创建可复用的组件,组件可以在未知上下文中使用,并且可以避免污染所处的外部作用域或不经意地污染内部作用域

作用域绑定策略

对于指令隔离作用域angularJS提供了几种方法能够将指令内部的隔离作用域,同指令外部的作用域进行数据绑定

  • @(or @attr) 使用@符号将指令本地作用域同DOM属性的值进行绑定,指令内部作用域可以使用外部作用域的变量,纯粹的值绑定
  • =(or =attr) 通过=可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定,就像普通的数据绑定一样,本地属性会反映出父数据模型中所发生的改变
  • &(or &attr) 通过&符号可以对父级作用域进行绑定,以便在其中运行函数。意味着对这个值进行设置时会生成一个指向父级作用域的包装函数,要使调用带有一个参数的父方法,我们需要传递一个对象,这个对象的键是参数的名称,值是要传递给参数的内容
demo.html

<!doctype html>
<html ng-app="freefedApp">
   <head>
        <title>angular应用demo</title>
        <script src="angular.js"></script>
        <script src="app.js"></script>
  </head>
  <body>
  <div ng-controller="freefedCtrl">
       发送邮件地址 : <input type="text" ng-model="from" />
       接收邮件地址 : <input type="text" ng-model="to" />
        <div email-directive from-email="{{from}}" to-email="to" on-send="sendMail(email)">
        </div>
   </div>
  </body>
</html>

 

app.js

/*声明module*/
var module = angular.module(‘freefedApp‘,[]);

/*声明控制器*/
module.controller(‘freefedCtrl‘,[‘$scope‘,function($scope){
      $scope.from = ‘zhangzhen@hoge.cn‘;
      $scope.sendMail = function(email){
          alert(‘我将发送邮件到‘ + email);
      };
}]);

/*声明指令*/
module.directive(‘emailDirective‘,function(){
      return {
           scope : {
               fromEmail : ‘@‘       //值绑定
               toEmail : ‘=‘         //和父级作用域属性进行双向绑定
               onSend : ‘&‘          //与父级作用域进行绑定
           },
           link : function( scope,el,attr ){
                scope.onSend( { email : scope. toEmail} );      //调用与onSend绑定的父级作用域的sendMail函数,并传递参数email的值给它
           }
      };
});

指令参数详解

  • restrict: String
    restrict是一个可选的参数。它告诉angularJS这个指令在DOM中可以何种形式被声明。默
    认angularJS认为restrict的值是A,即以属性的形式来进行声明;可选值如下:
    E(元素)
    A(属性,默认值)
     
    C(类名)
     
    M(注释)
  • priority: Number,
    优先级参数可以被设置为一个数值。大多数指令会忽略这个参数,使用默认值0,但也有些 场景设置高优先级是非常重要甚至是必须的。
    例如,ngRepeat将这个参数设置为1000,这样就可 10 以保证在同一元素上,它总是在其他指令之前被调用
  • terminal: Boolean,

    这个参数用来告诉angularJS停止运行当前元素上比本指令优先级低的指令。但同当前指令 优先级相同的指令还是会被执行。
    ?如果元素上某个指令设置了terminal参数并具有较高的优先级,就不要再用其他低优先级的 指令对其进行修饰了,因为不会被调用。
    但是具有相同优先级的指令还是会被继续调用。使用了terminal参数的例子是ngView和ngIf。ngIf的优先级略高于ngView,如果ngIf的表 达式值为true,
    ngView就可以被正常执行,但如果ngIf表达式的值为false,由于ngView的优先 级较低就不会被执行
  • template: String or Template Function:function(tElement, tAttrs) (...},

    template参数是可选的,必须被设置为以下两种形式之一:
    一段HTML文本;
    一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个代表模板的字符
    串。tElement和tAttrs中的t代表template,是相对于instance的。在讨论链接和编译 设置时会详细介绍,模板元素或属性与实例元素或属性之间的区别。
    angularJS会同处理HTML一样处理模板字符串。模板中可以通过大括号标记来访问作用域, 例如{{ expression }}。
    如果模板字符串中含有多个DOM元素,或者只由一个单独的文本节点构成,那它必须被包 含在一个父元素内。换句话说,必须存在一个根DOM元素:
         template: ‘         <div> <-- single root element -->             <a href="http://google.com">Click me</a>             <h1>When using two elements, wrap them in a parent element</h1>         </div>另外,注意每一行末尾的反斜线,这样angularJS才能正确解析多行字符串。在实际生产中, 更好的选择是使用templateUrl参数引用外部模板
  • templateUrl: String,

    templateUrl是可选的参数,可以是以下类型:
    一个代表外部HTML文件路径的字符串;
    一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个外部HTML文件路径的字符串,
    默认情况下,调用指令时会在后台通过Ajax来请求HTML模板文件
    模板加载是异步的,意味着编译和链接要暂停,等待模板加载完成。
    通过Ajax异步加载大量的模板将严重拖慢一个客户端应用的速度。为了避免延迟,可以在部 署应用之前对HTML模板进行缓存。
    在大多数场景下缓存都是一个非常好的选择,
    因为angularJS 通过减少请求数量提升了性能。模板加载后,angularJS会将它默认缓存到$templateCache服务中。
    在实际生产中,可以提前将模板缓存到一个定义模板的JavaScript文件中,这样就不需要通过XHR来加载模板了
  • replace: Boolean or String,

    replace是一个可选参数,如果设置了这个参数,值必须为true,因为默认值为false。
    默认值意味着模板会被当作子元素插入到调用此指令的元素内部:
         <div some-directive></div>
         .directive(‘someDirective‘, function() {
                 return {
                      template: ‘<div>some stuff here<div>‘
                 }; 
          });
    调用指令之后的结果如下(这是默认replace为false时的情况):
         <div some-directive>
             <div>some stuff here<div>
         </div> 如果replace被设置为了true:
         .directive(‘someDirective‘, function() {
                 return {
                      replace: true // 修饰过
    ??????                  template: ‘<div>some stuff here<div>‘
                 }; 
         ?});
    指令调用后的结果将是:
         <div>some stuff here<div>
  • scope: Boolean or Object,

    看上面指令作用域
  • transclude: Boolean,

  • controller: String orfunction(scope, element, attrs, transclude, otherInjectables) { ... },

    controller参数可以是一个字符串或一个函数。当设置为字符串时,会以字符串的值为名字, 
    来查找注册在应用中的控制器的构造函数</pre>
    * controllerAs: String,<pre>controllerAs参数用来设置控制器的别名,可以以此为名来发布控制器,我们可以在路由和指令中创建匿名控制器的强大 能力。
    这种能力可以将动态的对象创建成为控制器,并且这个对象是隔离的、易于测试的```
  • require: String,
  • link: function(scope, iElement, iAttrs) { ... },

    用link函数创建可以操作DOM的指令

angularJS中如何写自定义指令