Angular入门教程三
4.6指令(directive)
通过使用模板,我们可以把model和controller中的数据组装起来呈现给浏览器,还可以通过数据绑定,实时更新视图,让我们的页面变成动态的。
模板中可以使用的东西包括以下四种:
1.指令(directive):ng提供的或者自定义的标签和属性,用来增强HTML表现力;
2.标记(markup):即双大括号{{}},可将数据单向绑定到HTML中;
3.过滤器(filter):用来格式化输出数据;
4.表单控制:用来增强表单的验证功能。
其中,指令无疑是使用量最大的,ng内置了很多指令用来控制模板,如ng-repeat,ng-class,也有很多指令来帮你完成业务逻辑,如ng-controller,ng-model。
指令的几种使用方式如下:
l 作为标签:<my-dir></my-dir>
l 作为属性:<span my-dir="exp"></span>
l 作为注释:<!-- directive: my-dir exp -->
l 作为类名:<span class="my-dir: exp;"></span>
其实常用的就是作为标签和属性。
4.6.1样式相关的指令
既然模板就是普通的HTML,那我首要关心的就是样式的控制,元素的定位、字体、背景色等等如何可以灵活控制。下面来看看常用的样式控制指令。
1. ng-class
ng-class用来给元素绑定类名,其表达式的返回值可以是以下三种:
l 类名字符串,可以用空格分割多个类名,如’redtext boldtext’;
l 类名数组,数组中的每一项都会层叠起来生效;
l 一个名值对应的map,其键值为类名,值为boolean类型,当值为true时,该类会被加在元素上。
下面来看一个使用map的例子:
ng-class测试
红色 加粗 删除线
map:{redtext:{{red}}, boldtext:{{bold}}, striketext:{{strike}}}
如果你想拼接一个类名出来,可以使用插值表达式,如:
<div class=”{{style}}text”>字体样式测试</div>
然后在controller中指定style的值:
$scope.style = ‘red’;
注意我用了class而不是ng-class,这是不可以对换的,官方的文档也未做说明,姑且认为这是ng的语法规则吧。
与ng-class相近的,ng还提供了ng-class-odd、ng-class-even两个指令,用来配合ng-repeat分别在奇数列和偶数列使用对应的类。这个用来在表格中实现隔行换色再方便不过了。
2. ng-style
ng-style用来绑定元素的css样式,其表达式的返回值为一个js对象,键为css样式名,值为该样式对应的合法取值。用法比较简单:
<div ng-style="{color:'red'}">ng-style测试</div>
<div ng-style="style">ng-style测试</div>
$scope.style = {color:'red'};
3. ng-show,ng-hide
对于比较常用的元素显隐控制,ng也做了封装,ng-show和ng-hide的值为boolean类型的表达式,当值为true时,对应的show或hide生效。框架会用display:block和display:none来控制元素的显隐。
4.6.2表单控件功能相关指令
对于常用的表单控件功能,ng也做了封装,方便灵活控制。
ng-checked控制radio和checkbox的选中状态
ng-selected控制下拉框的选中状态
ng-disabled控制失效状态
ng-multiple控制多选
ng-readonly控制只读状态
以上指令的取值均为boolean类型,当值为true时相关状态生效,道理比较简单就不多做解释。注意: 上面的这些只是单向绑定,即只是从数据到模板,不能反作用于数据。要双向绑定,还是要使用 ng-model 。
4.6.3事件绑定相关指令
事件绑定是javascrpt中比较重要的一部分内容,ng对此也做了详细的封装,正如我们之前使用过的ng-click一样,事件的指令如下:
ng-click
ng-change
ng-dblclick
ng-mousedown
ng-mouseenter
ng-mouseleave
ng-mousemove
ng-mouseover
ng-mouseup
ng-submit
事件绑定指令的取值为函数,并且需要加上括号,例如:
<select ng-change=”change($event)”></select>
然后在controller中定义如下:
$scope.change = function($event){
alert($event.target);
//……………………
}
在模板中可以用变量$event将事件对象传递到controller中。
对于ng的这种设计,一些人有所质疑,视图与事件绑定混在一起到底好不好?我们不是要讲究视图与逻辑分离吗?如此一来,把事件的绑定又变回了内联的,岂不是历史的倒退。我也一样对此表示不解,因为不写onclick已经很多年。。。但既然已经存在了,我们不妨往合理的方向上想一想,或许ng的设计者压根就不想让模板成为单纯的视图层,本来就是想增强HTML,让它有一点业务能力。这么想的话似乎也能想通,好吧,先欺骗一下自己吧~
4.6.4特殊的ng-src和ng-href
在说明这两个指令的特殊之前,需要先了解一下ng的启动及执行过程,如下图:
1) 浏览器加载静态HTML文件并解析为DOM;
2) 浏览器加载angular.js文件;
3) angular监听DOMContentLoaded 事件,监听到时开始启动;
4) angular寻找ng-app指令,确定作用范围;
5) 找到app中定义的Module使用$injector服务进行依赖注入;
6) 根据$injector服务创建$compile服务用于编译;
7) $compile服务编译DOM中的指令、过滤器等;
8) 使用ng-init指令,将作用域中的变量进行替换;
9) 最后生成了我们在最终视图。
可以看到,ng框架是在DOMcontent加载完毕后才开始发挥作用。假如我们模板中有一张图片如下:
<img src="http://m.cnblogs.com/142260/”{{imgUrl}}” />
那么在页面开始加载到ng编译完成之前,页面上会一直显示一张错误的图片,因为路径{{imgUrl}}还未被替换。
为了避免这种情况,我们使用ng-src指令,这样在路径被正确得到之前就不会显示找不到图片。同理,<a>标签的href属性也需要换成ng-href,这样页面上就不会先出现一个地址错误的链接。
顺着这个思路再多想一点,我们在模板中使用{{}}显示数据时,在ng编译完成之前页面上岂不是会显示出大括号及里面的表达式?确实是这样。为了避免这个,ng中有一个与{{}}等同的指令:ng-bind,同样用于单向绑定,在页面刚加载的时候就不会显示出对用户无用的数据了。尽管这样你可能不但没舒心反而更纠结了,{{}}那么好用易理解,还不能用了不成?好消息是我们依然可以使用。因为我编写的是单页面应用,页面只会在加载index.html的时
候出这个问题,只需在index.html中的模板中换成ng-bind就行。其他的模板是我们动态加载的,就可以放心使用{{}}了。
4.6.5 自定义指令示例
下面我们来解析下指令的例子(例07)。
1.首先,我们定义一个名为userInfo的指令:
demoApp.directive('userInfo',function(){
return {
restrict : 'E',
templateUrl : 'userInfoTemplate.html',
replace : true,
transclude : true,
scope : {
mytitle : '=etitle'
},
link : function(scope,element,attrs){
scope.showText = false;
scope.toggleText = function(){
scope.showText = ! scope.showText;
}
}
};
})
Restrict为'E':用作标签;replace为true:用模板替换当前标签;transclude为true:将当前元素的内容转移到模板中;scope 为 {mytitle : '=etitle'}:定义一个名为mytitle的MODEL,其值指向当前元素的etitle属性;templateUrl为'userInfoTemplate.html':模板内容为ng-template定义ID为userInfoTemplate.html的内容;link:指定所包含的行为。其具体的说明及其他参数,请参考:6.2指令详解。
2. userInfoTemplate.html模板为:
<script type="text/ng-template" id="userInfoTemplate.html">
<div class="mybox">
<div class="mytitle" style="cursor: pointer;" ng-click="toggleText()">
{ {mytitle} }
</div>
<div ng-transclude ng-show="showText">
</div>
</div>
</script>
将当前元素的内容添加到有ng-transclude属性的这个DIV下,默认是隐藏的。
3.Controller信息:
demoApp.controller("test7Controller", function($scope){
$scope.title = '个人简介';
$scope.text = '大家好,我正在研究AngularJs,欢迎大家与我交流。';
$scope.updateInfo = function (){
$scope.title = '个人信息';
$scope.text = '大家好,今天天气真好!';
}
});
4.指令使用方式(View信息)为:
<user-info etitle="title">{ {text} }</user-info>
Etitle指向Controller中的$scope.title。注意命名方式:指令名为userInfo,对应的标签为user-info。
4.7服务(service)
4.7.1服务介绍
服务这个概念其实并不陌生,在其他语言中如java便有这样的概念,其作用就是对外提供某个特定的功能,如消息服务,文件压缩服务等,是一个独立的模块。ng的服务是这样定义的:
Angular services are singletons objects or functions that carry out specific tasks common to web apps.
它是一个单例对象或函数,对外提供特定的功能。
首先是一个单例,即无论这个服务被注入到任何地方,对象始终只有一个实例。
其次这与我们自己定义一个function然后在其他地方调用不同,因为服务被定义在一个模块中,所以其使用范围是可以被我们管理的。ng的避免全局变量污染意识非常强。
ng提供了很多内置的服务,可以到API中查看http://docs.angularjs.org/api/。知道了概念,我们来拉一个service出来溜溜,看看到底是个什么用法。
我们在controller中直接声明$location服务,这依靠ng的依赖注入机制。$location提供地址栏相关的服务,我们在此只是简单的获取当前的地址。
服务的使用是如此简单,我们可以把服务注入到controller、指令或者是其他服务中。
4.7.2自定义服务
如同指令一样,系统内置的服务以$开头,我们也可以自己定义一个服务。定义服务的方式有如下几种:
l 使用系统内置的$provide服务;
l 使用Module的factory方法;
l 使用Module的service方法。
下面通过一个小例子来分别试验一下。我们定义一个名为remoteData服务,它可以从远程获取数据,这也是我们在程序中经常使用的功能。不过我这里没有远程服务器,就写死一点数据模拟一下。
//使用$provide来定义
var app = angular.module('MyApp', [], function($provide) {
$provide.factory('remoteData', function() {
var data = {name:'n',value:'v'};
return data;
});
});
//使用factory方法
app.factory('remoteData',function(){
var data = {name:'n',value:'v'};
return data;
});
//使用service方法
app.service('remoteData',function(){
this.name = 'n';
this.value = 'v';
});
Module的factory和$provide的factory方法是一模一样的,从官网文档看它们其实就是一回事。至于Module内部是如何调用的,我此处并不打算深究,我只要知道怎么用就好了。
再看Module的service方法,它没有return任何东西,是因为service方法本身返回一个构造器,系统会自动使用new关键字来创建出一个对象。所以我们看到在构造器函数内可以使用this,这样调用该服务的地方便可以直接通过remoteData.name来访问数据了。
4.7.3管理服务的依赖关系
服务与服务中间可以有依赖关系,例如我们这里定义一个名为validate的服务,它的作用是验证数据是否合法,它需要依赖我们从远程获取数据的服务remoteData。代码如下:
在factory的参数中,我们可以直接传入服务remoteData,ng的依赖注入机制便帮我们做好了其他工作。不过一定要保证这个参数的名称与服务名称一致,ng是根据名称来识别的。若参数的名次与服务名称不一致,你就必须显示的声明一下,方式如下:
app.factory('validate',['remoteData',function(remoteDataService){
return function(){
if(remoteDataService.name=='n'){
alert('验证通过');
}
};
}]);
我们在controller中注入服务也是同样的道理,使用的名称需要与服务名称一致才可以正确注入。否则,你必须使用$inject来手动指定注入的服务。比如:
function testC(scope,rd){
scope.getData = function(){
alert('name:'+rd.name+' value:'+rd.value);
}
}
testC.$inject = ['$scope','remoteData'];
在controller中注入服务,也可以在定义controller时使用数组作为第二个参数,在此处
把服务注入进去,这样在函数体中使用不一致的服务名称也是可以的,不过要确保注入的顺序是一致的,如:
app.controller('testC',['$scope','remoteData',function($scope,rd){
$scope.getData = function(){
alert('name:'+rd.name+' value:'+rd.value);
}
}]);
4.7.4 自定义服务示例
接下来让我们看下例子(例08 自定义服务)代码,自定义userService服务:
demoApp.factory('userService', ['$http', function($http) {
var doGetUser = function(userId, path) {
//return $http({
//method: 'JSONP',
//url: path
//});
/*手动指定数据*/
var data = {userId:"woshishui",userName:"我是谁",userInfo:"我是谁!我是谁!"};;
if(userId=='zhangsan'){
data = {userId:"zhangsan",userName:"张三",userInfo:"我是张三,我为自己"};
}else if(userId=='lisi'){
data = {userId:"lisi",userName:"李四",userInfo:"我是李四,我为卿狂!"};
}
return data;
}
return {
/*userService对外暴露的函数,可有多个*/
getUser: function(userId) {
return doGetUser(userId, '../xxx/xxx.action');
}
};
}]);
我们创建了一个只有一个方法的userService,getUser为这个服务从后台获取用户信息的函数,并且对外暴露。当然,由于这是一个静态的例子,无法访问后台,那么我们便制定其返回的数据。
然后我们把这个服务添加到我们的controller中。我们建立一个controller并加载(或者注入)userService作为运行时依赖,我们把service的名字作为参数传递给controller 函数:
demoApp.controller("test8Controller", function($scope,userService){
/*文章信息*/
$scope.articles = [{
title : "爱飞像风",
userId : "zhangsan",
userName : "张三"
},{
title : "无法停止的雨",
userId : "lisi",
userName : "李四"
}];
$scope.showUserInfo = false;//显示作者详细信息开关
$scope.currentUser = {}; //当前选中的作者
$scope.getUserInfo = function(userId){
$scope.currentUser = userService.getUser(userId);
//调用 userService的getUser函数
$scope.showUserInfo = true;
setTimeout(function(){//定时器:隐藏作者详细信息
$scope.showUserInfo = false;
},3000);
}
});
我们的userService注入到我们的test8Controller后,我们就可以像使用其他服务(我们前面提到的$http服务)一样的使用userService了。
相关的HTML代码如下:
/* View HTML*/
<tr ng-repeat="article_ in articles">
<td>
{{article_.title}}
</td>
<td>
<a href="javascript:void(0);" target="_blank" rel="nofollow">
</td>
</tr>
......
<div ng-show="showUserInfo">
用户ID:{{currentUser.userId}}<br/>
用户名:{{currentUser.userName}}<br/>
用户简介:{{currentUser.userInfo}}<br/>
</div>
Angular入门教程三的更多相关文章
- 无废话ExtJs 入门教程三[窗体:Window组件]
无废话ExtJs 入门教程三[窗体:Window组件] extjs技术交流,欢迎加群(201926085) 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3 ...
- PySide——Python图形化界面入门教程(三)
PySide——Python图形化界面入门教程(三) ——使用内建新号和槽 ——Using Built-In Signals and Slots 上一个教程中,我们学习了如何创建和建立交互widget ...
- Elasticsearch入门教程(三):Elasticsearch索引&映射
原文:Elasticsearch入门教程(三):Elasticsearch索引&映射 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文 ...
- RabbitMQ入门教程(三):Hello World
原文:RabbitMQ入门教程(三):Hello World 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
- JasperReports入门教程(三):Paramters,Fields和Detail基本组件介绍
JasperReports入门教程(三):Paramter,Field和Detail基本组件介绍 前言 前两篇博客带领大家进行了入门,做出了第一个例子.也解决了中文打印的问题.大家跟着例子也做出了de ...
- WebGL入门教程(三)-webgl动画
前面文章: WebGL入门教程(一)-初识webgl WebGL入门教程(二)-webgl绘制三角形 WebGL动画有移动.旋转和缩放,我们将移动.旋转和缩放图形,然后将其绘制到屏幕上,称为变换(tr ...
- 中文代码示例之Angular入门教程尝试
原址: https://zhuanlan.zhihu.com/p/30853705 原文: 中文代码示例教程之Angular尝试 为了检验中文命名在Angular中的支持程度, 把Angular官方入 ...
- 2017-11-07 中文代码示例之Angular入门教程尝试
"中文编程"知乎专栏原址 原文: 中文代码示例教程之Angular尝试 为了检验中文命名在Angular中的支持程度, 把Angular官方入门教程的示例代码中尽量使用了中文命名. ...
- 无废话SharePoint入门教程三[创建网站集和网站]
一.前言 前两篇文章讲解了什么是SharePoint,并且介绍了在SharePoint中一些常用的概念.但概念终究是概念,我们还是要脚踏实地的去动手实践.下面的文章对于了解SharePoint的人来说 ...
随机推荐
- 架构师养成记--29.redis开篇
主要有从下几点讲解 NOSQL(Redis) 简介.redis安装与部署 Redis基础事件类型详解 Redis高级命令 Redis与java的使用 Redis集群搭建 Redis集群与spring的 ...
- 【Jenkins】定时构建语法
跟cron定时任务语法基本类似 一.字段有哪些 每行包含5个字段,用制表符或空格隔开,从左至右依次是: 分 时 天 月 星期 二.每个字段的取值范围 分钟 (0–59) 时 (0–23) 天 (1–3 ...
- mysql中字符串1.1/1.2/1.2.2/1.2.5排序问题
1.创建查询函数:(split_pid为函数名称) create function split_pid(str varchar (1000),delimiter varchar(1)) returns ...
- 关于Nginx启动成功,浏览器不能访问的解决办法
本人初学Nginx,第一天配置成功并能通过浏览器进行访问. 第二天重新打开,将Nginx启动,但是浏览器却访问不了. 执行 ps aux|grep nginx ,执行结果如下,的确Nginx服务已经启 ...
- springcloud(九)-Feign使用Hystrix
前言 上一篇我们使用注解@HystrixCommond的fallbackMethod属性实现回退.然而,Feign是以接口形式工作的,它没有方法体,上一篇讲解的方式显然不适用于Feign. 那么Fei ...
- linux 创建软链接和硬链接
Linux 系统中有软链接和硬链接两种特殊的“文件”. 软链接可以看作是Windows中的快捷方式,可以让你快速链接到目标档案或目录. 硬链接则透过文件系统的inode来产生新档名,而不是产生新档案. ...
- 003javascript语句
javascript语句和java差不多,注意==和===区别 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" " ...
- javac的词法分析
1.词法分析将Java源文件的字符流转变为对应的Token流. JavacParser类规定了哪些词是符合Java语言规范规定的词,而具体读取和归类不同词法的操作由Scanner类来完成. ...
- nginx 配置静态资源路径(url不同于static path)
目的 用nginx做静态资源代理可以减少请求对后台服务器的压力,使响应更加迅速. 配置 情景一 url : 127.0.0.1:8000/images ...
- [中英对照]Booting Process in Linux RHEL 7 | Linux RHEL 7启动过程
Booting Process in Linux RHEL 7 | Linux RHEL 7启动过程 In this post, I will guide you booting process in ...