• AngularJS -- 指令(创建自定义指令)


    点击查看AngularJS系列目录
    转载请注明出处:http://www.cnblogs.com/leosx/


    什么是指令

    注:本指南是针对已经熟悉AngularJS基础知识的开发人员。如果你才刚刚开始,我建议查看系列教程。(这章是Angular的重点)

    指令是一个Dom元素上的标签(和元素上的属性, CSS 类样式一样,属于这个Dom元素),它告诉AngualrJS的HTML 编译器($compile),去附加一个行为到这个Dom元素上去,这个行为可以改变这个Dom元素,或者这个Dom元素的子元素。

    AngularJS 有一套自己内置的指令,如:ngBind,ngModel,ngClass等等...你也可以自定义自己的指令。当Angular应用程序起来之后,会先去加载Dom树,然后去匹配你的指令,然后执行。

    HTML 编译器 -- 对于Angularjs来说,编译意味着递归去给HTML添加一些事件监听,让DOM元素和Angualr之间可以进行交互,相互作用。这里使用编译这个术语的原因是:添加事件监听这个过程,刚好反应了我们C#,Java等编程语言将源代码编译成应用的这个过程。

    匹配指令

    在我们编写我们的第一个自定义指令之前,我能需要知道AngularJS的HTML编译器是怎么确定在什么时候使用我们自定义的指令的。

    下面这个例子中,我们可以看到<Input>元素匹配到了 ngModel 这个指令。

    <input ng-model="foo">

    下面这个例子也是匹配到了 ngModel 这个指令:

    <input data-ng:model="foo">

    AngularJS使用元素标签类型(eg:input)和属性名称来确定哪个元素匹配到了哪个指令。标准的指令(eg: ngModel)是区分大小写的,使用了驼峰命名法则。然而,由于HTML是不区分大小写的,所以后来也不区分大小写了…(感觉等于没说…) 但是通常我们都是使用  [-]  这个破折号来代表一个指令(通常,并不是必须)。

    一般的使用方法如下

    1.使用  x-  或者  data-  在元素或者属性前。

    2.使用  : 或者  - 或者 _ 来代替了驼峰命名法则。

    <div ng-app="docsBindExample">
      <div ng-controller="Controller">
        Hello <input ng-model='name'> <hr/>
        <span ng-bind="name"></span> <br/>
        <span ng:bind="name"></span> <br/>
        <span ng_bind="name"></span> <br/>
        <span data-ng-bind="name"></span> <br/>
        <span x-ng-bind="name"></span> <br/>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsBindExample', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)';
      }]);
    })();
    </script>

    效果图:

    directiveOne

    $compile编译器会根据元素名称,属性,CSS类名,甚至是注释去匹配指令

    下面展示了一个指令可以再哪些地方被引用:

    <my-dir></my-dir>
    <span my-dir="exp"></span>
    <!-- directive: my-dir exp -->
    <span class="my-dir: exp;"></span>

    建议1:  最好的使用指令方式是通过标签名,属性名,而不是使用注释和CSS样式类名。这样做可以让编译器更加容易的去正确匹配指令。

    建议2:  注释指令通常用来解决Dom API的限制,进行跨越多个元素的操作。比如在 <Table> 元素内,去跨越多个元素进行操作。Angular1.2中引入了ng-repeat-startng-repeat-end指令,它们可以更好的去解决这个问题。鼓励开发者使用它们,而不是使用注释指令去解决这样的问题。

    文本和属性绑定

    在编译过程当中,编译器会根据文本的属性值和使用了$interpolate服务的属性去查看这个HTML元素是否包含嵌入式的表达式。如果有,那么这些表达式会被注册到监视器中,并且会进入$digest阶段。下面是一个使用了$interpolate 表达式的示例:

    <a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>

    ngAttr属性绑定

    我们的浏览器有时候会挑剔什么是他们认为又要的属性值(也就是属性值不是你随便写的) 想想看下面的例子:

    <svg>
    <circle cx="{{cx}}"></circle>
    </svg>

    我们很希望Angular允许我们这样绑定,但是当我们打开浏览器控制台的时候,你就会看到如下错误:

    Error: Invalid value for attribute cx="{{cx}}".

    这是因为SVG DOM API的限制原因,你就不能写成: cx=”{{cs}}”了,而应该使用: ng-attr-cx 来解决这个问题。

    如果一个属性的绑定的前缀是 ngAttr 而不是正规的 ng-attr-这种形式,那么在binding期间,它会被应用到相应的没有前缀的属性上去(eg: <circle ngAttr-cx=”a”></circle> 它是不会被应用到cx属性上去的)。因为当使用ngAttr前缀的时候,对于$interpolate阶段来说,任何标识都还不起作用。所以在这个阶段中所有的string结果都是 undefined的,既然是undefined的,那么对应的这个属性会被移除,而不再继续添加到元素上去。

    我们可以如下去解决上面提到的问题:

    <svg>
    <circle ng-attr-cx="{{cx}}"></circle>
    </svg>

    注册指令

    我们先来谈谈和注册指令相关的API,和controller一样,指令也是被注册到模块(modules)上的,创建指令的API是: module.directive(指令名称,功能函数)。module.directive(direName,func)  ,这个func方法需要返回一个 配置参数对象,通常是一个Json对象,来告诉$compile编译器,进行这个指令匹配时的行为及动作.

    这个func功能函数只会在当编译器第一次匹配到指令的时候执行一次。你可以在这里执行任何的初始化工作。这个func函数是被 $injector.invoke方法调用的。

    友情提示:通常,为了避免与未来一些指令冲突,我们自定义的时候,都最好有一个自己的前缀。。。

    模板扩展指令

    我们想想,如果你现在需要一个模板,去展示客户的信息,而且这个模板会重复出现很多次,当你要改变一个地方,你就得去改很多次。这个是一个很好的情景让你去使用指令(directive)去简化你的模板(你该使用模板扩展指令了)。

    下面,我们就是用一个例子来说明:

    <div ng-app="docsSimpleDirective">
      <div ng-controller="Controller">
        <div my-customer></div>
      </div>
    <div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsSimpleDirective', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.customer = {
          name: 'Naomi',
          address: '1600 Amphitheatre'
        };
      }])
      .directive('myCustomer', function() {
        return {
          template: 'Name: {{customer.name}} Address: {{customer.address}}'
        };
      });
    })();
    </script>

    效果图:

    directiveTwo

    当$compile编译器编译,并且连接了<div my-customer></div>之后,它将会继续尝试匹配这个元素的子元素。这就意味着你可以篡改其它指令哦!一会儿我们来看下面的例子。

    还有需要注意的,上面的例子当中,我们返回了参数 template,当模板大小改变的时候,这是很烦人的哦!你的字符串会很长,很长! 哈哈!好像邪恶了。 除非你的模板就像上面例子一样,确实特别小,否则,我们还是建议你把模板吓到一个单独的HTML文件当中去,然后再指令的func中,我们返回的参数中,使用templateUrl选项,来指定我们的模板路径。

    <div>
      <div ng-controller="Controller">
        <div my-customer></div>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsTemplateUrlDirective', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.customer = {
          name: 'Naomi',
          address: '1600 Amphitheatre'
        };
      }])
      .directive('myCustomer', function() {
        return {
          templateUrl: 'my-customer.html'
        };
      });
    })();
    </script>

    my-customer.html的内容:

    Name: {{customer.name}} Address: {{customer.address}}

    效果图:

    directiveTwo

    其实,templateUrl选项也可以是一个函数,返回要加载的HTML模板的URL路径,Angular会自动调用templateUrl函数,并且传入两个参数:ele(对应的元素), 和一个与这个元素相关的attr对象。

    注意:在scope没有初始化之前,你是不能够在templateUrl函数中访问scope的,所以说,Controller的初始化一定是要在使用templateUrl函数之前。

    <div ng-app="docsTemplateUrlDirective">
      <div ng-controller="Controller">
        <div my-customer type="name"></div>
        <div my-customer type="address"></div>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsTemplateUrlDirective', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.customer = {
          name: 'Naomi',
          address: '1600 Amphitheatre'
        };
      }])
      .directive('myCustomer', function() {
        return {
          templateUrl: function(elem, attr){
            return 'customer-'+attr.type+'.html';
          }
        };
      });
    })();
    </script>

    customer-name.html

    Name: {{customer.name}}

    customer-address.html

    Address: {{customer.address}}

    效果图:

    directiveThree

    再次注意:默认情况下,你创建的指令仅限于用于属性或者元素,为了创建一个可以用于类(class)的触发器,你需要使用到restrict选项。

    restrict选项的几个经典选项:

    ‘A’ -  仅仅匹配属性名

    ‘E’ -   仅仅去匹配元素名

    ‘C’ -   仅仅匹配class名

    友情提示:这些选项可以根据需要进行组合的哦!!

    ‘AEC’- 它可以匹配属性(attr)或者元素(ele)或者类名(class)。

    我们来写一个例子,让我们的这个指令只能去匹配元素名,也就是说这个指令只能用于元素上。

    <div ng-app="docsRestrictDirective">
      <div ng-controller="Controller">
        <my-customer></my-customer>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
       angular.module('docsRestrictDirective', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.customer = {
          name: 'Naomi',
          address: '1600 Amphitheatre'
        };
      }])
      .directive('myCustomer', function() {
        return {
          restrict: 'E', // 注意这里哦!
          templateUrl: 'my-customer.html'
        };
      });
    })();
    </script>

    my-customer.html内容如下:

    Name: {{customer.name}} Address: {{customer.address}}

    效果图:

    directiveFour

    更多的关于restrict选项,你可以参考API文档(你知道的,有可能访问不了哦!肿么破,这里不说哈!)

    更大的问题来了。什么时候我该用属性,什么时候我们该用元素呢?  不出意外,其实你应该能知道,什么时候该用什么。 当我们要创建一个控件的时候,那我们就得使用元素匹配方式。当你需要给你现在的元素增加一些新功能,进行修饰,扩展的时候,我们就得使用属性了。不多解释…

    指令独立的scope

    大家可以想一想,上面说的关于指令的使用的例子,就会发现它有个致命的缺陷,一个Scope(也就是一个Controller)里面你只能使用一次我们自定义的myCustomer指令。这样的话,我们如果想要重复的使用这个指令的时候,你就得声明与之对应个数的Controller,Oh~~ my god  !!!  你肯定瞬间不想使用Angular了,不错不要着急,会有解决方法的。

    我们可以使用给每个directive(指令)一个独立的scope来进行分离,然后将外部Controller中的scope映射到directive(指令)内部的独立的scope上去。这样,我们就可以调用自己独立的scope啦! 是不是很好哦!要实现上面的独立,AngularJS给我们提供了一个directive的scope参数,看下面示例,你就明白了!

    <div ng-app="docsIsolateScopeDirective">
      <div ng-controller="Controller">
        <my-customer info="naomi"></my-customer>
        <hr>
        <my-customer info="igor"></my-customer>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsIsolateScopeDirective', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
        $scope.igor = { name: 'Igor', address: '123 Somewhere' };
      }])
      .directive('myCustomer', function() {
        return {
          restrict: 'E',
          scope: {
            customerInfo: '=info'
          },
          templateUrl: 'my-customer-iso.html'
        };
      });
    })();
    </script>

    my-customer-iso.html内容如下:

    Name: {{customerInfo.name}} Address: {{customerInfo.address}}

    效果图:

    directivefive

    我们来解释一下上面,在<my-customer>元素中,我们给它的info属性中,绑定了Controller中的Scope中的naomi属性上去了。 第二个<my-customer>元素的info属性我们是给绑定了igor属性。

    我们来仔细看看scope选项:

    // ...
    scope:{
       customerInfo: '=info'
    },
    // ...

    scope选项中,包涵了所有关于这个指令独立的scope信息,在上面例子中,它只包涵了一个属性:

    customerInfo 是这个指令独立的scope的属性名,它的值为  =info   ,这是在告诉$compile编译器,去把这个属性绑定到一个叫做info的属性上去。。。

    注意:上面的绑定是一个很标准的绑定方式,但是如果你想绑定成这样:<div bind-to-this=”thing”>,你需要将customerInfo的值设置为  =bindToThis    (也就是驼峰命名法则)

    我们来考虑还有一种情况,如果说你想就让绑定的属性名和你这个指令独立的scope的属性名一样的话,你可以使用下面这个简写语法:

    ...
    scope: {
      // same as customer: '=customer'
      customer: '='
    },
    ...

    除了可以将不同的数据绑定到directive 独立的scope上之外,使用独立的scope还有另外一个效果.

    不解释,先来段代码:

    <div ng-app='docsIsolationExample'>
      <div ng-controller="Controller">
        <my-customer info="naomi"></my-customer>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsIsolationExample', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
        $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
      }])
      .directive('myCustomer', function() {
        return {
          restrict: 'E',
          scope: {
            customerInfo: '=info'
          },
          templateUrl: 'my-customer-plus-vojta.html'
        };
      });
    })();
    </script>

    my-customer-plus-vojta.html 内容如下:

    Name: {{customerInfo.name}} Address: {{customerInfo.address}}
    <hr>
    Name: {{vojta.name}} Address: {{vojta.address}}

    效果图如下:

    directivesix

    请注意: {{vojta.name}} 和 {{vojta.address}} 值是空的。也就是他们根本没有被定义。虽然我们在controller中的scope中定义了vojta属性,但是,它在我们自定义的directive(指令)中,是不可用的。。。

    顾名思义,在这个directive的独立scope中,除了实体模型你需要明确的增加之外,你可以添加任何的东西到这个独立的scope中,那么这样的话,我们就可以在这里进行重构组件,这样的话,我们就可以防止控件改变了你的模型的状态,当然,除了你明确的指定将这个模型传入进来了,你都传进来了,就可以改变了。

    注意:在一般情况下,一个scope是从上级的scope派生来的,但是这里是一个特例哦!  directive(指令)中的scope不是那样的,如果你想要了解更多的关于定义指令的对象- scope 你可以看看这里。

    建议: 当我们想要让我们自定义的组件在你的整个Angular应用程序中重用的话,你就要使用独立的scope,但是,如果你只使用一次的话,就可以不用了。不过个人建议,最好还是使用吧! *^_^*

    创建一个指令,去操作DOM

    在这个例子当中。我们去创建一个指令,用于显示当前的时间。一秒更新一次,我们就需要更新DOM去显示当前时间。

    通常,我们如果想要在指令中去修改DOM元素的话(Angular不建议你直接去操作DOM),会使用 link 选项 ,link 选项的签名如下:

    function link(scope, element, attrs){ ...... }
       [ scope ]是一个Angular的scope对象
       [ element ]是这个指令所匹配到的那个元素。
       [ attrs ] 是一个规范的key-value键值对儿的hash对象

    在link 方法中,我们想每秒钟更新一下显示时间, 可以使用Angular自带的 $interval 服务,相比之下,它要比  $timeout 服务更加好用,而且更加的易于端对端测试,它会保证在你的测试完成之前,你的$timeout 里面的所有执行都已完成,而且,它也可以保证当你把directive(指令)移除了之后,会自动的把 $interval 服务给卸载掉,从而不会造成内存的泄露。

    是骡子是马,咱们还是直接拉出来溜溜!! 直接上代码!

    <div ng-ap="docsTimeDirective">
      <div ng-controller="Controller">
        Date format: <input ng-model="format"> <hr/>
        Current time is: <span my-current-time="format"></span>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsTimeDirective', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.format = 'M/d/yy h:mm:ss a';
      }])
      .directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) {
    
        function link(scope, element, attrs) {
          var format,
              timeoutId;
    
          function updateTime() {
            element.text(dateFilter(new Date(), format));
          }
    
          scope.$watch(attrs.myCurrentTime, function(value) {
            format = value;
            updateTime();
          });
    
          element.on('$destroy', function() {
            $interval.cancel(timeoutId);
          });
    
          // start the UI update process; save the timeoutId for canceling
          timeoutId = $interval(function() {
            updateTime(); // update DOM
          }, 1000);
        }
    
        return {
          link: link
        };
      }]);
    })();
    </script>

    看看效果图(自己去实践哈,这里不做jif的动态图了哈):

    directiveseven

    这里有几个需要注意的地方。 就像module.contoller API一样,module.directive函数也是依赖注入的,因此,我们可以注入 $interval 和 dateFilter(过滤器),然后再link 函数中进行使用了。

    我们注册了事件 element.on(‘$destory’, … )    在 $destory  事件中,发生了什么事情呢?

    在AngularJS里有几个特别的事件。当一个DOM节点在Angular的编译器中要被销毁,会触发 $destory  事件。同样的,当一个AngularJS的 scope 对象被销毁的时候,也会广播一个 $destory 事件,给那些监听scope的对象。

    通过监听这个事件,你可以去删除哪些可能会导致内存溢出的监听器。已注册监听的scope和元素(element)会在它们被销毁的时候自动清理掉,我们是不用去手动销毁的。但是,如果你你注册了一个服务,或者一个监听DOM节点的监听器,而且它们还被删除了,那么,你就得自己手动去清理。否则可能会造成内存泄露。

    建议:指令应该由他们自己去清理的。也就是说,你需要调用 element.on( ‘$destory’, … ) 或者 scope.$on( ‘$destory’, …)  去运行一个清理函数(func),来保证当这个指令被清除的时候,会清除掉和它相关的资源。

    我们来创建一个封装了其它元素的指令:

    现在你知道了怎么传递一个model到 directive上的独立scope上去。 但是,有的时候,我们需要传递整个模板,而不只是一个字符串或者一个对象而已,那怎么办呢?  假设我们现在需要创建一个对话框组件。对话框应该可以显示任意对象,该怎么实现呢?  这需要用到另外的一个选项:transclude    直接上代码:

    <div ng-app="docsTransclusionDirective">
      <div ng-controller="Controller">
        <my-dialog>Check out the contents, {{name}}!</my-dialog>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsTransclusionDirective', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.name = 'Tobias';
      }])
      .directive('myDialog', function() {
        return {
          restrict: 'E',
          transclude: true,
          templateUrl: 'my-dialog.html'
        };
      });
    })();
    </script>

    my-dialog.html页面内容:

    <div class="alert" ng-transclude>
    </div>

    我们来看看效果图:

    directiveseight

    那这个 transclude 选项做了些什么事情呢?   transclude 选项会让指令去访问来自指令之外的 scope 。而不是去访问自己内部的scope 。

    还是使用例子来解释下吧! 下列中,请注意,我们添加了一个 link 函数,它重新设置了scope中的name属性的值为 “Jeff”,你猜猜在 {{name}} 绑定中,会显示什么呢?

    <div ng-app="docsTransclusionExample">
      <div ng-controller="Controller">
        <my-dialog>Check out the contents, {{name}}!</my-dialog>
      </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsTransclusionExample', [])
      .controller('Controller', ['$scope', function($scope) {
        $scope.name = 'Tobias';
      }])
      .directive('myDialog', function() {
        return {
          restrict: 'E',
          transclude: true,
          scope: {},
          templateUrl: 'my-dialog.html',
          link: function (scope, element) {
            scope.name = 'Jeff';
          }
        };
      });
    })();
    </script>

    my-dialog.html 中的内容如下:

    <div class="alert" ng-transclude>
    </div>

    一般来说,我们期望 {{name}} 里面显示的是 “Jeff”,但是,but,事实上,它显示的依然是 “Tobias”。选项 transclude 改变了 scope的嵌套方式,它会让{{name}} 指令去访问指令外的scope,而不是去访问指令内部的scope 。

    注意:如果你的指令没有创建自己的scope,那么你再在link 中, scope.name= ‘Jeff’的话,就会看到输出的结果是 “Jeff”了,因为这样相当于重新定义了外部scope的name属性值为 “Jeff”。不要绕晕了哦!

    这个对于那些需要包涵一些不同内容(内容可变)的指令来说非常有意义哦! 没有这个特性的话,你就得分别创建多个不同的实例了。

    建议: 从我们的使用来看就可以知道, transclude选项的使用情景应该是当你的指令内容中,需要包含经常变化或者是不定的地方。

    接下来,我们来创建一个有按钮的对话框,它允许我们去绑定一些我们自定义的行为上去,直接上代码:

    <div ng-app="docsIsoFnBindExample">
        <div ng-controller="Controller">
          <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
            Check out the contents, {{name}}!
          </my-dialog>
        </div>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsIsoFnBindExample', [])
      .controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
        $scope.name = 'Tobias';
        $scope.hideDialog = function () {
          $scope.dialogIsHidden = true;
          $timeout(function () {
            $scope.dialogIsHidden = false;
          }, 2000);
        };
      }])
      .directive('myDialog', function() {
        return {
          restrict: 'E',
          transclude: true,
          scope: {
            'close': '&onClose'
          },
          templateUrl: 'my-dialog-close.html'
        };
      });
    })();
    </script>

    my-dialog-close.html 内容如下:

    <div class="alert">
      <a href class="close" ng-click="close()">&times;</a>
      <div ng-transclude></div>
    </div>

    再来看看效果图:

    leosx010

    当我们点击关闭按钮的时候,它会把下面那句话隐藏掉。两秒钟后,再显示出来,自己亲自去试!

    正如我们所知道一样,我们可以在指令的scope中定义一些函数(func),然后在scope的上下文中去调用它,但是注意的是,这个函数并需先定义。

    我们之前看到了怎么样在scope选项中使用  =attr 。但是上面的例子当中,我们使用了 &attr 实例。这里的这个 & 绑定(别看只是一个符号,它是一个绑定哦!) 允许指令在原始scope的特定时间,去触发调用一个表达式。这个表达式不限制,任意表达式都行,包括那些包涵了方法,函数调用的表达式。正因如此, & 绑定符,是一个很理想的 绑定 指令的回调函数 的方式。

    当用户点击对话框中的X按钮的时候,指令的close 函数将被调用,由于ng-click的执行,事实上,它会调用它指令自己独立的scope,而那个scope选项(是选项哦,不要搞晕了),又去调用了上下文中父级scope的 hideDialog() 方法。因此实现了控制器中的hideDialog功能。

    建议: 当你想要给你的自定义指令暴露一个绑定行为的API的时候,你就可以使用scope选项 的 &attr 。

    创建一个增加事件监听的指令

    以前,我们使用 link 选项函数去创建一个指令去操作DOM元素。下面,我们来创建一个能够反映使用它的元素的事件的指令。这个示例中,我们会创建一个可以让元素拖动的指令。

    先上例子:

    <div ng-app="dragModule">
       <span my-draggable>Drag ME</span>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('dragModule', [])
      .directive('myDraggable', ['$document', function($document) {
        return function(scope, element, attr) {
          var startX = 0, startY = 0, x = 0, y = 0;
    
          element.css({
           position: 'relative',
           border: '1px solid red',
           backgroundColor: 'lightgrey',
           cursor: 'pointer'
          });
    
          element.on('mousedown', function(event) {
            // Prevent default dragging of selected content
            event.preventDefault();
            startX = event.pageX - x;
            startY = event.pageY - y;
            $document.on('mousemove', mousemove);
            $document.on('mouseup', mouseup);
          });
    
          function mousemove(event) {
            y = event.pageY - startY;
            x = event.pageX - startX;
            element.css({
              top: y + 'px',
              left:  x + 'px'
            });
          }
    
          function mouseup() {
            $document.off('mousemove', mousemove);
            $document.off('mouseup', mouseup);
          }
        };
      }]);
    })();
    </script>

    效果图:

    leosx011

    创建一个用于通信的指令

    还是先上例子吧!

    <div ng-app="docsTabsExample">
      <my-tabs>
        <my-pane title="Hello">
          <h4>Hello</h4>
          <p>Lorem ipsum dolor sit amet</p>
        </my-pane>
        <my-pane title="World">
          <h4>World</h4>
          <em>Mauris elementum elementum enim at suscipit.</em>
          <p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
        </my-pane>
      </my-tabs>
    </div>
    <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
      (function(){
        angular.module('docsTabsExample', [])
      .directive('myTabs', function() {
        return {
          restrict: 'E',
          transclude: true,
          scope: {},
          controller: function($scope) {
            var panes = $scope.panes = [];
    
            $scope.select = function(pane) {
              angular.forEach(panes, function(pane) {
                pane.selected = false;
              });
              pane.selected = true;
            };
    
            this.addPane = function(pane) {
              if (panes.length === 0) {
                $scope.select(pane);
              }
              panes.push(pane);
            };
          },
          templateUrl: 'my-tabs.html'
        };
      })
      .directive('myPane', function() {
        return {
          require: '^myTabs',
          restrict: 'E',
          transclude: true,
          scope: {
            title: '@'
          },
          link: function(scope, element, attrs, tabsCtrl) {
            tabsCtrl.addPane(scope);
          },
          templateUrl: 'my-pane.html'
        };
      });
    })();
    </script>

    my-tabs.html   内容如下:

    <div class="tabbable">
      <ul class="nav nav-tabs">
        <li ng-repeat="pane in panes" ng-class="{active:pane.selected}">
          <a href="" ng-click="select(pane)">{{pane.title}}</a>
        </li>
      </ul>
      <div class="tab-content" ng-transclude></div>
    </div>

    my-pane.html  内容如下:

    <div class="tab-pane" ng-show="selected" ng-transclude>
    </div>

    效果图:

    leosx012

    myPane 指令中,使用到了一个 require选择,其值为:  ^myTabs  。当一个指令使用了这个选项的, $compile 编译器会需要让你提供一个 contorller ,不然的话,它会抛出一个异常的。  使用 ^  前缀意味着这个指令会在它的父级元素(如果没有 ^ 前缀的话,它会在自己的元素里面查找Controller)里面去查找Controller。

    那么,这个 myTabs controller又是从哪里来的呢? 指令可以使用Controller选项去指定controller从哪里来! 就像上面例子一下,  myTabs 指令使用了这个选项。 这个选项的值和AngularJS的控制器( ngController )写法一样,这个选项也可以附加一些模板给这个指令。

    如果有必要引用一个controller或者任何的函数,然后将他们绑定到模板的controller的scope上去的话,你可以使用 controllerAs 选项来为controller指定一个别名。  你需要给指令定义一个用于使用的scope配置,在指令作为一个组件来使用的时候,这是特别有用的哦!

    再来回顾一下myPane 的定义,注意 link函数的最后的一个参数: tabsCtrl。 当一个指令需要一个控制器的时候,在 link 函数中,会传到第四个参数中去。我们可以在第四次参数中得到。 利用这一点,myPane可以调用myTabs中的addPane功能了。

    如果你需要多个控制器的话,可以给 require选项传入一个数组。如下:

    angular.module('docsTabsExample', [])
    .directive('myPane', function() {
      return {
        require: ['^myTabs', '^ngModel'],
        restrict: 'E',
        transclude: true,
        scope: {
          title: '@'
        },
        link: function(scope, element, attrs, controllers) {
          var tabsCtrl = controllers[0],
              modelCtrl = controllers[1];
    
          tabsCtrl.addPane(scope);
        },
        templateUrl: 'my-pane.html'
      };
    });

    有些读者可能很想知道 link 函数和 controller之间的区别。 它们最基本的区别是,controller是一个暴露的API, 而link 函数是让元素和控制器进行交互的。

    建议: 当你确定你需要向外,向其他指令暴露一个API的时候,你就得使用controller,其它的情况,还是使用 link比较好!!!

    简单说明一下,这一章很重要,但是这里呢,我们只是写了一些指令的用例,但是每个用例都是你去创造更多指令的一个很不错的起点哦!!  发挥你的想要把!

    如果你有兴趣了解一下编译过程的话,你可以点击这里

    如果你想要查看一下 $compile API 编译器提供的详细的指令的话,你可以点击 这里

  • 相关阅读:
    2.socket编程
    1网络编程基础概念
    vim笔记
    mysql示例及练习2
    mysql的示例及练习
    自己封装的mysql应用类示例
    mysql3_pymysql
    mysql2
    mysql1
    python之列表与集合
  • 原文地址:https://www.cnblogs.com/leosx/p/4048281.html
Copyright © 2020-2023  润新知