AngularJS实例详解Directive(指令)机制

AngularJS是为了克服HTML在构建应用上的不足而设计的。AngularJS中指令的重要性是不言而喻的,指令可以让我们创建自己的HTML标记,它将自定义元素变成了一个一个的模块,极大的体现了前端开发中的模块化模式,并提高了代码的易读性和重用性。指令除了可以定义为HTML标记,也可以定义为标签的元素、类和注释。

1. 小介绍

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

 

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

 

<div>
    <span>Something</span>
</div>

 

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

 

<parentTag>
    <childTag1>子面板1</childTag1>
    <childTag2>子面板2</childTag2>
</parentTag>

 

这个是不是和JSP或者Struts等等框架里面的taglib很像呢?

 

呃,实际上确实是这样的,只不过AngularJS是使用JavaScript来实现的。正因为如此,很多taglib做不到的功能,使用它就都可以做到,比如访问N层scope里面的对象之类的事情。

2. AngularJS Directive实例

2.1 实例1:从最简单的开始

<html ng-app='DiretiveDemo'>
    <body>
        <hello></hello>
    </body>

    <script type="text/javascript" src="scripts/angular.js"></script>
    <script type="text/javascript" src="scripts/diretiveDemo.js"></script>
</html>

 

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

 

来看这段温馨的JS代码:

 

var appModule = angular.module('DiretiveDemo', [], angular.noop);
appModule.directive('hello', function() {
    return {
        restrict: 'E',
        template: '<div>Hello, this is hello.</div>',
        replace: true
    };
});

在浏览器中访问这个页面,我们就可以看到这样的内容:


 

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

 


 

可以看到,<hello>这个东东已经被<div>Hello, this is hello.</div>这个标签替换掉了,这也是以上JS代码里面replace:true这行配置的作用,代码里面的template配置 项当然就是我们要的div标签啦。 你也可以尝试下replace:false这个配置,看看和true有什么区别。至于restrict:'E'这个配置项的含义,请看下表:

 
 



 

 

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

 

2.2 实例2:transclude(变换)

 

先看例子,JS代码:

 

var appModule = angular.module('DiretiveDemo', [], angular.noop);
appModule.directive('hello', function(){
  return {
    template: '<div>Hello Here <span ng-transclude></span></div>',
    restrict: 'E',
    transclude: true
    }
});

 

HTML代码:

 

<!DOCTYPE html>
<html ng-app='DiretiveDemo'>
<head>
	<title>Directive Demo</title>
</head>
<body>
	<hello>
	    <br>
	    <span>Original Content.</span>
	    <span>Content is Here.</span>
	</hello>
	<hr>	
	<hello></hello>
	<script type="text/javascript" src="scripts/angular.js"></script>
	<script type="text/javascript" src="scripts/diretiveDemo.js"></script>
</body>
</html>

 

运行效果如下:

 



 

 

生成的HTML标签结构如下:

 



 

 

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

 

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

 

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

 

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

 

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

 

2.3 实例3:关于compile和link

 

JS代码:

 

var appModule = angular.module('DiretiveDemo', [], angular.noop);
appModule.directive('hello', function(){
  return {
    template: '<div>Hello, this is hello.</div>',
    restrict: 'E',
    replace: true
    }
});

appModule.controller('MyCtrl', function($scope){
	$scope.ones=[1, 2, 3, 4, 5];
})

 

HTML代码:

<!DOCTYPE html>
<html ng-app='DiretiveDemo'>
<head>
	<title>Directive Demo</title>
</head>
<body ng-controller="MyCtrl">
	<div ng-repeat="one in ones">
		{{one}}.<hello></hello>
	</div>
	
	<script type="text/javascript" src="scripts/angular.js"></script>
	<script type="text/javascript" src="scripts/diretiveDemo.js"></script>
</body>
</html>

 显示效果 + html标签结构:


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

 

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

 

简而言之,compile阶段进行标签解析和变换,link阶段进行数据绑定等操作。

 

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

 

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

 

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

 

JS代码:

 

var expanderModule=angular.module('ExpanderDemo', [])
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代码:

 

<!DOCTYPE html>
<html ng-app='ExpanderDemo'>
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	<title>Expander Demo</title>
	<link rel="stylesheet" type="text/css" href="styles/expanderDemo.css"/>
</head>
<body ng-controller="SomeController">
	 <div ng-controller='SomeController'>
            <expander class='expander' expander-title='title'>
                {{text}}
            </expander>
        </div>
	
	<script type="text/javascript" src="scripts/angular.js"></script>
	<script type="text/javascript" src="scripts/expanderDemo.js"></script>
</body>
</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;
    }
}

 

2.5 实例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中的数据。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值