• 《AngularJS》5个实例详解Directive(指令)机制


    本文整理并扩展了《AngularJS》这本书第六章里面的内容,此书近期即将由电子工业出版社出版,敬请期待口令:Angular

    1.一点小说明

    指令的作用:实现语义化标签

    我们常用的HTML标签是这样的:

    1 <div>
    2     <span>一点点内容</span>
    3 </div>

    而使用AngularJS的directive(指令)机制,我们可以实现这样的东西:

    1 <tabpanel>
    2     <panel>子面板1</panel>
    3     <panel>子面板2</panel>
    4 </tabpanel>

    很多人可能要惊呼,这货和JSP或者Struts等等框架里面的taglib很像啊!

    呃,说实话,实际上就是这样的,只不过这里是使用JavaScript来实现的。正因为如此,所以很多taglib做不到的功能,使用它就都可以做到,比如访问N层scope里面的对象之类的事情(参见后面第5个例子)。

    2.实例1:从最简单的开始

    1 <html ng-app='app'>
    2     <body>
    3         <hello></hello>
    4     </body>
    5     <script src="../angular-1.0.3/angular.min.js"></script>
    6     <script src="HelloDirect.js"></script>
    7 </html>

    对于以上代码里面的<hello>标签,浏览器显然是不认识的,它唯一能做的事情就是无视这个标签。那么,为了让浏览器能够认识这个标签,我们需要使用Angular来定义一个hello指令(本质上说就是自己来把<hello>这种玩意儿替换成浏览器能识别的那些标准HTML标签)。

    来看这段温馨的JS代码:

    1 var appModule = angular.module('app', []);
    2 appModule.directive('hello', function() {
    3     return {
    4         restrict: 'E',
    5         template: '<div>Hi there</div>',
    6         replace: true
    7     };
    8 });

    以上代码大概看两眼就可以了,不要太在意细节。

    然后我们就可以在浏览器里面看到这样的内容:



     

    实际产生的标签结构是这样的:



     

    可以看到,<hello>这个东东已经被<div>Hi there</div>这个标签替换掉了,这也是以上JS代码里面replace:true这行配置的作用,代码里面的template配置 项当然就是我们要的div标签啦,至于restrict:'E'这个配置项的含义,请看下表:



     

    ok,看完上面的表格,对于restrict这个属性相信你已经秒懂了,那么我们来玩儿点花样吧。如果我们需要替换的HTML标签很长,显然不能用 拼接字符串的方式来写,这时候我们可以用templateUrl来替代template,从而可以把模板写到一个独立的HTML文件中。

    3.实例2:transclude(变换)

    先看例子,JS代码:

    1 var appModule = angular.module('app', []);
    2     appModule.directive('hello', function() {
    3     return {
    4         restrict: 'E',
    5         template: '<div>Hi there <span ng-transclude></span></div>',
    6         transclude: true
    7     };
    8 });

    HTML代码:

     1 <html ng-app='app'>
     2     <head>
     3         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
     4     </head>
     5     <body>
     6         <hello>
     7             <br/>
     8             <span>原始的内容,</span><br/>
     9             <span>还会在这里。</span>
    10         </hello>
    11         <hello>
    12         </hello>
    13     </body>
    14     <script src="../angular-1.0.3/angular.min.js"></script>
    15     <script src="Transclude.js"></script>
    16 </html>

    运行效果如下:


    生成的HTML标签结构如下:


     

    和第一个例子对比,这个例子的JS和HTML代码都略有不同,JS代码里面多了一个transclude: true,HTML代码里面在<hello>内部出现了子标签。

    按照我们在第一个例子中的说法,指令的作用是把我们自定义的语义化标签替换成浏览器能够认识的HTML标签。那好,如果我们自定义的标签内部出现了子标签,应该如何去处理呢?很显然,transclude就是用来处理这种情况的。

    对于当前这个例子,transclude的作用可以简化地理解成:把<hello>标签替换成我们所编写的HTML模板,但是<hello>标签内部的内容保持不变

    很显然,由于我们没有加replace:true选项,所以<hello>标签还在,没有被替换掉。同时,通过这个例子你还还会发现一 个暗藏的属性,那就是浏览器实际上非常智能,虽然它并不认识<hello>这个标签,但是页面没有出错,它只是默默地把这个标签忽略掉了!怎 么样?是不是碉堡了?

    你可以自己在上面的JS代码里面加上replace:true,然后再看生成的HTML结构。

    4.实例3:关于compile和link

    JS代码:

     1 var appModule = angular.module('app', []);
     2 appModule.directive('hello', function() {
     3     return {
     4         restrict: 'E',
     5         template: '<span>Hi there</span>',
     6         replace: true
     7     };
     8 });
     9 appModule.controller('MyController',function($scope) {
    10     $scope.things = [1,2,3,4,5,6];
    11 });

    HTML代码:

    1 <html ng-app='app'>
    2     <body ng-controller='MyController'>
    3         <div ng-repeat='thing in things'>
    4             {{thing}}.<hello></hello>
    5         </div>
    6     </body>
    7     <script src="../angular-1.0.3/angular.min.js"></script>
    8     <script src="CompileAndLink.js"></script>
    9 </html>

    呃,这个例子是用来解释一点点理论的,所以单纯看效果可能看不出个鸟。

    如前所述,指令的本质其实是一个替换过程。好,既然如此,Angular到底是如何进行替换的呢?嗯嗯,这个过程分2个阶段,也就是本节标题所说的compile(编译)和link(连接)了。

    简而言之,compile阶段进行标签解析和变换,link阶段进行数据绑定等操作。这里面更加细节的处理过程请参见《AngularJS》这本书中的解析,这里就不赘述了(呃,实际上是因为解释起来很长很麻烦,叔懒得在这儿说了

     )。

    那么,知道这件事情有什么用途呢?

    比方说,你有一些事件需要绑定到某个元素上,那么你需要提供一个link函数,做法请看下一个例子。

    5.实例4:一个复杂一点的例子Expander

    这是《AngularJS》这本书里面提供的一个例子,但是书里面没有给出完整的可运行代码,所以这里给出来,大家参考一下。

    JS代码:

     1 var expanderModule=angular.module('expanderModule', [])
     2 expanderModule.directive('expander', function() {
     3     return {
     4         restrict : 'EA',
     5         replace : true,
     6         transclude : true,
     7         scope : {
     8             title : '=expanderTitle'
     9         },
    10         template : '<div>'
    11                  + '<div class="title" ng-click="toggle()">{{title}}</div>'
    12                  + '<div class="body" ng-show="showMe" ng-transclude></div>'
    13                  + '</div>',
    14         link : function(scope, element, attrs) {
    15             scope.showMe = false;
    16             scope.toggle = function toggle() {
    17                 scope.showMe = !scope.showMe;
    18             }
    19         }
    20     }
    21 });
    22 expanderModule.controller('SomeController',function($scope) {
    23     $scope.title = '点击展开';
    24     $scope.text = '这里是内部的内容。';
    25 });

    HTML代码:

     1 <html ng-app='expanderModule'>
     2     <head>
     3         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
     4         <script src="../angular-1.0.3/angular.min.js"></script>
     5         <link rel="stylesheet" type="text/css" href="ExpanderSimple.css"/>
     6     </head>
     7     <body>
     8         <div ng-controller='SomeController'>
     9             <expander class='expander' expander-title='title'>
    10                 {{text}}
    11             </expander>
    12         </div>
    13     </body>
    14     <script src="ExpanderSimple.js"></script>
    15 </html>

    CSS代码:

     1 .expander {
     2     border: 1px solid black;
     3     width: 250px;
     4 }
     5 .expander>.title {
     6     background-color: black;
     7     color: white;
     8     padding: .1em .3em;
     9     cursor: pointer;
    10 }
    11 
    12 .expander>.body {
    13     padding: .1em .3em;
    14 }

    运行效果如下:

    注意一下JS代码里面的这一段:

    1 link : function(scope, element, attrs) {
    2     scope.showMe = false;
    3     scope.toggle = function toggle() {
    4         scope.showMe = !scope.showMe;
    5     }
    6 }

    自己跑一跑例子,研究一番,不多解释。

    6.实例5:一个综合的例子

    JS代码:

     1 var expModule=angular.module('expanderModule',[])
     2 expModule.directive('accordion', function() {
     3     return {
     4         restrict : 'EA',
     5         replace : true,
     6         transclude : true,
     7         template : '<div ng-transclude></div>',
     8         controller : function() {
     9             var expanders = [];
    10             this.gotOpened = function(selectedExpander) {
    11                 angular.forEach(expanders, function(expander) {
    12                     if (selectedExpander != expander) {
    13                         expander.showMe = false;
    14                     }
    15                 });
    16             }
    17             this.addExpander = function(expander) {
    18                 expanders.push(expander);
    19             }
    20         }
    21     }
    22 });
    23 expModule.directive('expander', function() {
    24     return {
    25         restrict : 'EA',
    26         replace : true,
    27         transclude : true,
    28         require : '^?accordion',
    29         scope : {
    30             title : '=expanderTitle'
    31         },
    32         template : '<div>'
    33                    + '<div class="title" ng-click="toggle()">{{title}}</div>'
    34                    + '<div class="body" ng-show="showMe" ng-transclude></div>'
    35                    + '</div>',
    36         link : function(scope, element, attrs, accordionController) {
    37             scope.showMe = false;
    38             accordionController.addExpander(scope);
    39             scope.toggle = function toggle() {
    40                 scope.showMe = !scope.showMe;
    41                 accordionController.gotOpened(scope);
    42             }
    43         }
    44     }
    45 });
    46 expModule.controller("SomeController",function($scope) {
    47     $scope.expanders = [{
    48         title : 'Click me to expand',
    49         text : 'Hi there folks, I am the content that was hidden but is now shown.'
    50     }, {
    51         title : 'Click this',
    52         text : 'I am even better text than you have seen previously'
    53     }, {
    54         title : 'Test',
    55         text : 'test'
    56     }];
    57 });

    HTML代码:

     1 <html ng-app="expanderModule">
     2     <head>
     3         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
     4         <script src="../angular-1.0.3/angular.min.js"></script>
     5         <link rel="stylesheet" type="text/css" href="Accordion.css"/>
     6     </head>
     7     <body ng-controller='SomeController' >
     8         <accordion>
     9             <expander class='expander' ng-repeat='expander in expanders' expander-title='expander.title'>
    10                 {{expander.text}}
    11             </expander>
    12         </accordion>
    13     </body>
    14     <script src="Accordion.js"></script>
    15 </html>

    CSS代码:

     1 .expander {
     2     border: 1px solid black;
     3     width: 250px;
     4 }
     5 .expander>.title {
     6     background-color: black;
     7     color: white;
     8     padding: .1em .3em;
     9     cursor: pointer;
    10 }
    11 
    12 .expander>.body {
    13     padding: .1em .3em;
    14 }

    运行效果:


    这个例子主要的难点在于如何在子Expander里面访问外层Accordion的scope中的数据,这一点解释起来略复杂,这里就不展开了,详细描述参见《AngularJS》一书 

     

    AngularJS官方站点在这里:http://angularjs.org/

    [全文完]

    其它相关内容:

    1、OReilly的《AngularJS》已由电子工业出版社出版

    http://damoqiongqiu.iteye.com/blog/1965167

    2、对比Angular/jQueryUI/Extjs:没有一个框架是万能的

    http://damoqiongqiu.iteye.com/blog/1922004

    3、AngularJS表单基础

    http://damoqiongqiu.iteye.com/blog/1920191

    4、AngularJS Form 进阶:远程校验和自定义输入项

    http://damoqiongqiu.iteye.com/blog/1920993

    5、AngularJS:在Windows上安装Yeoman

    http://damoqiongqiu.iteye.com/blog/1885371

    6、使用JsTestDriver实现JavaScript单元测试

    http://damoqiongqiu.iteye.com/blog/1924415

  • 相关阅读:
    Core的学习三:容器的使用实例
    Core的学习二:【四步完成】.Net Core中Log4net的使用,配置,【框架.NET5】
    C#7 的一些新语法
    C#6 的一些新语法
    Core的学习一:Core部署在IIS下
    C# 特性【Attribute】【什么是特性?以及特性的一些修饰】
    C#反射
    泛型 -Generic 【why,原理,与普通方法,object的性能对比如何?泛型类、泛型方法、泛型接口、泛型委托,泛型约束,协变 逆变,泛型缓存】
    springboot通过切面编程实现系统请求操作日志记录
    Linux 【安装配置VM虚拟机】
  • 原文地址:https://www.cnblogs.com/joyco773/p/5677447.html
Copyright © 2020-2023  润新知