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

转自大漠穷秋:http://damoqiongqiu.iteye.com/blog/1917971

感谢作者分享!

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

大漠穷秋

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

1.一点小说明

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

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

<div>
    <span>一点点内容</span>
</div>

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

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

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

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

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

<html ng-app=‘app‘>
    <body>
        <hello></hello>
    </body>

    <script src="../angular-1.0.3/angular.min.js"></script>
    <script src="HelloDirect.js"></script>
</html>

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

来看这段温馨的JS代码:

var appModule = angular.module(‘app‘, []);
appModule.directive(‘hello‘, function() {
    return {
        restrict: ‘E‘,
        template: ‘<div>Hi there</div>‘,
        replace: true
    };
});

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

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

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

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

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

3.实例2:transclude(变换)

先看例子,JS代码:

var appModule = angular.module(‘app‘, []);
    appModule.directive(‘hello‘, function() {
    return {
        restrict: ‘E‘,
        template: ‘<div>Hi there <span ng-transclude></span></div>‘,
        transclude: true
    };
});

HTML代码:

<html ng-app=‘app‘>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    </head>
    <body>
        <hello>
            <br/>
            <span>原始的内容,</span><br/>
            <span>还会在这里。</span>
        </hello>
        <hello>
        </hello>
    </body>

    <script src="../angular-1.0.3/angular.min.js"></script>
    <script src="Transclude.js"></script>
</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代码:

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

HTML代码:

<html ng-app=‘app‘>
    <body ng-controller=‘MyController‘>
        <div ng-repeat=‘thing in things‘>
            {{thing}}.<hello></hello>
        </div>
    </body>

    <script src="../angular-1.0.3/angular.min.js"></script>
    <script src="CompileAndLink.js"></script>
</html>

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

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

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

 )。

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

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

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

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

JS代码:

var expanderModule=angular.module(‘expanderModule‘, [])
expanderModule.directive(‘expander‘, function() {
    return {
        restrict : ‘EA‘,
        replace : true,
        transclude : true,
        scope : {
            title : ‘=expanderTitle‘
        },
        template : ‘<div>‘
                 + ‘<div class="title" ng-click="toggle()">{{title}}</div>‘
                 + ‘<div class="body" ng-show="showMe" ng-transclude></div>‘
                 + ‘</div>‘,
        link : function(scope, element, attrs) {
            scope.showMe = false;
            scope.toggle = function toggle() {
                scope.showMe = !scope.showMe;
            }
        }
    }
});
expanderModule.controller(‘SomeController‘,function($scope) {
    $scope.title = ‘点击展开‘;
    $scope.text = ‘这里是内部的内容。‘;
});

HTML代码:

<html ng-app=‘expanderModule‘>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <script src="../angular-1.0.3/angular.min.js"></script>
        <link rel="stylesheet" type="text/css" href="ExpanderSimple.css"/>
    </head>
    <body>
        <div ng-controller=‘SomeController‘>
            <expander class=‘expander‘ expander-title=‘title‘>
                {{text}}
            </expander>
        </div>
    </body>
    <script src="ExpanderSimple.js"></script>
</html>

CSS代码:

.expander {
    border: 1px solid black;
    width: 250px;
}

.expander>.title {
    background-color: black;
    color: white;
    padding: .1em .3em;
    cursor: pointer;
}

.expander>.body {
    padding: .1em .3em;
}

运行效果如下:

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

link : function(scope, element, attrs) {
    scope.showMe = false;
    scope.toggle = function toggle() {
        scope.showMe = !scope.showMe;
    }
}

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

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

JS代码:

var expModule=angular.module(‘expanderModule‘,[])
expModule.directive(‘accordion‘, function() {
    return {
        restrict : ‘EA‘,
        replace : true,
        transclude : true,
        template : ‘<div ng-transclude></div>‘,
        controller : function() {
            var expanders = [];
            this.gotOpened = function(selectedExpander) {
                angular.forEach(expanders, function(expander) {
                    if (selectedExpander != expander) {
                        expander.showMe = false;
                    }
                });
            }
            this.addExpander = function(expander) {
                expanders.push(expander);
            }
        }
    }
});

expModule.directive(‘expander‘, function() {
    return {
        restrict : ‘EA‘,
        replace : true,
        transclude : true,
        require : ‘^?accordion‘,
        scope : {
            title : ‘=expanderTitle‘
        },
        template : ‘<div>‘
                   + ‘<div class="title" ng-click="toggle()">{{title}}</div>‘
                   + ‘<div class="body" ng-show="showMe" ng-transclude></div>‘
                   + ‘</div>‘,
        link : function(scope, element, attrs, accordionController) {
            scope.showMe = false;
            accordionController.addExpander(scope);
            scope.toggle = function toggle() {
                scope.showMe = !scope.showMe;
                accordionController.gotOpened(scope);
            }
        }
    }
});

expModule.controller("SomeController",function($scope) {
    $scope.expanders = [{
        title : ‘Click me to expand‘,
        text : ‘Hi there folks, I am the content that was hidden but is now shown.‘
    }, {
        title : ‘Click this‘,
        text : ‘I am even better text than you have seen previously‘
    }, {
        title : ‘Test‘,
        text : ‘test‘
    }];
});

HTML代码:

<html ng-app="expanderModule">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <script src="../angular-1.0.3/angular.min.js"></script>
        <link rel="stylesheet" type="text/css" href="Accordion.css"/>
    </head>
    <body ng-controller=‘SomeController‘ >
        <accordion>
            <expander class=‘expander‘ ng-repeat=‘expander in expanders‘ expander-title=‘expander.title‘>
                {{expander.text}}
            </expander>
        </accordion>
    </body>
    <script src="Accordion.js"></script>
</html>

CSS代码:

.expander {
    border: 1px solid black;
    width: 250px;
}

.expander>.title {
    background-color: black;
    color: white;
    padding: .1em .3em;
    cursor: pointer;
}

.expander>.body {
    padding: .1em .3em;
}

运行效果:

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

AngularJS官方站点在这里

时间: 2024-08-07 06:27:01

《AngularJS》5个实例详解Directive(指令)机制(转)的相关文章

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

本文整理并扩展了<AngularJS>这本书第六章里面的内容,此书近期即将由电子工业出版社出版,敬请期待口令:Angular 1.一点小说明 指令的作用:实现语义化标签 我们常用的HTML标签是这样的: <div> <span>一点点内容</span> </div> 而使用AngularJS的directive(指令)机制,我们可以实现这样的东西: <tabpanel> <panel>子面板1</panel>

Vue 实例详解与生命周期

Vue 实例详解与生命周期 Vue 的实例是 Vue 框架的入口,其实也就是前端的 ViewModel,它包含了页面中的业务逻辑处理.数据模型等,当然它也有自己的一系列的生命周期的事件钩子,辅助我们进行对整个 Vue 实例生成.编译.挂着.销毁等过程进行 js 控制. Vue 实例初始化的选项配置对象详解 前面我们已经用了很多次 new Vue({...})的代码,而且 Vue 初始化的选项都已经用了data.methods.el.computedd等,估计您看到这里时,应该已经都明白了他们的作

php扩展imagick实例详解

imagick是一个可以供PHP调用ImageMagick功能的PHP扩展.使用这个扩展可以使PHP具备和ImageMagick相同的功能. PHP建图通常都用GD库,因为是内置的不需要在服务器上额外安装插件,所以用起来比较省心,但是如果你的程序主要的功能就是处理图像,那麼就不建议用GD了,因为GD不但低效能而且能力也比较弱,佔用的系统资源也颇多,另外GD的creatfrom也有bug,而imagick却是一个很好的替代品,为此最近把我的一个项目由GD改成了imagick,但是改完之后出现了一些

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(四)

这一章大象将详细分析web层代码,以及使用Spring MVC的注解及其用法和其它相关知识来实现控制器功能.     之前在使用Struts2实现MVC的注解时,是借助struts2-convention这个插件,如今我们使用Spring自带的spring-webmvc组件来实现同样的功能,而且比之以前更简单.另外,还省掉了整合两个框架带来的不稳定因素.     对于Spring MVC框架,我主要讲一下它的常用注解,再结合一些示例进行说明,方便大家能够快速理解.     一.Spring MV

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(五)

这是本系列的最后一篇,主要讲一下FreeMarker模板引擎的基本概念与常用指令的使用方式.     一.FreemMarker基本概念     FreemMarker是一个用Java语言编写的模板引擎,它是一个基于模板来生成文本输出的一个工具.是除了JSP之外被使用得最多的页面模板技术之一,另一个比较有名的模板则是Velocity.     用户可以使用FreeMarker来生成所需要的内容,通常由Java提供数据模型,FreeMarker通过模板引擎渲染数据模型,这样最终得到我们想要的内容.

23、磁盘管理—磁盘阵列(RAID)实例详解

磁盘阵列(RAID)实例详解 raid技术分类 软raid技术 硬raid技术 Raid和lvm的区别 为什么选择用raid RAID详解 RAID-0 RAID-1 RAID-5 Raid-10 Raid的管理 案例:创建一个raid10+冗余盘 磁盘阵列(RAID)实例详解 Raid(磁盘阵列)级别介绍 Raid有"廉价磁盘冗余阵列"的意思,就是利用多块廉价的硬盘组成磁盘组,让数据分部储存在这些硬盘里面,从而达到读取和写入加速的目的:也可以用作数据的冗余,当某块硬盘损毁后,其他硬盘

Cocos2d-x 3.X手游开发实例详解

Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰然网创始人杨雍力荐) 于浩洋 著   ISBN 978-7-121-23998-4 2014年9月出版 定价:59.00元 356页 16开 编辑推荐 以Cocos2d-x V3.0为框架全面讲解手游开发的知识和方法 以热门游戏2048.卡牌为例,完整再现手游的开发过程 Cocos2d-x作者之一林

实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(一)

在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用.虽然QuerySet的文档中已经详细说明了,但本文试图从QuerySet触发的SQL语句来分析工作方式,从而进一步了解Django具体的运作方式. 本来打算写成一篇单独的文章的,但是写完select_related()之后发现长度已经有点长了,所以还是写成系列,大概在两到三篇.整个完成之后将会在这里添加上

boost asio异步读写网络聊天程序客户端 实例详解

// // chat_client.cpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://ww