前端见微知著AngularJS备忘篇:温故而知新,可以为师矣
话说以前JQuery刚出来的时候,真的是对个人的冲击蛮大的。记得当时我买的第一本书就是《锋利的JQuery》,藉由这本书开始,我从此以后的项目基本用上了JQuery,其给我带来的便利性是不可言喻的,至少在当时,我奉之若掌上明珠。但是做了几年开发以后,逐渐发现其不便利的地方,尤其是频繁操纵Dom的场合。写一个五级联动,不仅要操作ajax,而且要操纵dom,更需要兼顾ajax请求完成的顺序。开始用的五层嵌套来完成的,发现代码太难看,然后重构成了利用TimeOut来进行操纵的场合,结果发现在网速很差的情况下总会出现加载错误的情况出现。即便最后改用了Promise来实现了ajax的按顺序加载,但是依然改不了那一坨坨的js操纵dom的代码在我心里留下的稍微恶心的感觉。因为那时候我真的觉得干了那么多年的程序了,不能写出个简洁明了的代码来,真的是挺有失落感的。从这几年的编程生涯看来,开始编程的时候,啥都不管,只要能用就行;有点追求的时候,拿个框架用用,总算是提高了品味;真正到了会编程的时候,才觉得简洁是最美好的东西,正所谓:少年听雨歌楼上,红烛昏罗帐。壮年听雨客舟中。江阔云低、断雁叫西风。而今听雨僧庐下。鬓已星星也。悲欢离合总无情。一任阶前、点滴到天明。
而今编程,以简洁为要,所以任何简洁的东西,都能引起我的注意,一不小心,AngularJS成了首选。
首先来说明一下AngularJS适用的场合:单页面的复杂程序,和用户交互能力比较强的页面。尤其适合CRUD类的页面。所以在这里我们可以看出其适用场合。如果单单是为了展示数据什么的,我看直接就免了AngularJS吧,毕竟可以在页面直接操纵一下,免了写Controller的烦恼。
然后需要说明的是AngularJS属于MVVM和MVC混合的产物。之所以说属于MVVM,是因为她可以监听到数据的变化,当前的任何数据变化,都会反映到界面上。其次就是她也是利用Controller来策动Model和View之间进行展示的。大家可以去专门详细看一看MVVM和MVC的架构思想,我这里主要是抛砖,至于怎么引玉就在于各位看官了。
下面我们就来一一分解其主要的知识点,如果这些知识点Hit到了你,但愿能够引发共鸣。
一、Module,字面意思是模块化,也就是体现当前模块的作用范围
一个页面上必须有一个利用ng-app指明的module,这样就能明白当前模块作用的dom元素的范围。假如我们放在了body元素上,那么作用范围就是整个body;如果放在了div上,那么作用范围就是当前整个div。切记一定不要忘记加这个属性,否则你后面的程序写的再多,再好,你怎么写都不会有任何结果的。
//定义Module
var app = angular.module("myApp", []);
//定义作用范围,这里是整个Body
<body ng-app="myApp">
二、Controller,控制器,用于协调Model和View显示的地方
app.controller('MainController', ['$scope', function($scope) {
$scope.title = 'Top Sellers in Books';
}]);
在这里,我们在app的module上定义了一个名称为MainController的控制器,并注入了$scope对象。需要说明的是,在Controller中,注入的对象都是通过string字符串的形式追加进来的,AngularJS会自动根据这些字串,然后反射成相应的对象。
在HTML中,我们增加此Controller的作用范围:
<div class="main" ng-controller="MainController">
那么在这种情况下,MainController将会在Class=Main的DIV上生效。
我们将title字段绑定到DIV内部的元素:
<h1>{{ title }}</h1>
需要说明的是,这个属于单项绑定,双向绑定我们会在后面介绍。
通过这样的绑定,当页面进行渲染的时候,controller中的值就会被推送到前台页面中进行展示。
三、各种Filter
在AngularJS中,filter是非常有用的一种功能,先看看简单的filter:
app.controller("MainController",["$scope",function($scope){
$scope.title = 'Great Heros';
$scope.promo = 'I have a dream';
$scope.product = {
name: 'The Book of Trees',
price: 19,
pubdate: new Date('2014', '03', '08')
}
}]);
然后在前台绑定并进行格式化操作:
<div class="col-md-6">
<div class="thumbnail">
<img src="img/the-book-of-trees.jpg">
<p class="title">{{product.name|uppercase}}</p>
<p class="price">{{product.price |currency }}</p>
<p class="date">{{product.pubdate|date}}</p>
</div>
</div>
从HTML绑定代码我们可以看到,我用了竖线分隔符,后面跟上不同的关键字,就相当于做了filter操作。对于名称,我用uppercase将其转换成了大写的,比如说(THE BOOK OF TREES);对于price,我用currency关键字将其格式化成了货币,比如说($19.00);而利用date关键字,我将pubdate转换成了时间格式,比如说(Apr 8, 2014)。
然后我们再看看稍微复杂一些的filter操作。
首先,我们可以自定义filter,请看如下操作:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>module Demo</title>
<style>
ul{
list-style: none;
display: block;
clear:both;
} ul li{ float:left; padding:10px;} </style>
</head>
<body>
<div ng-controller="myController"> <h3>RSA team {{ team }}</h3> <h2>RSA team name include "Ethan" {{ team | filter:{name:'Ethan'} }}</h2> <h2>RSA join team in 2015 {{ team | filter:join2015}}</h2> <ul ng-repeat = "item in team | orderBy: 'name'"> <li>{{ item.name | lowercase | capitalize}}</li>
<li>{{ item.join | date:'yyyy'}}</li>
</ul> <ul ng-repeat = "item in team | filter:join2015 |orderBy: 'name'">
<li>{{ $index }}</li>
<li>{{ item.name | lowercase | capitalize}}</li>
<li>{{ item.join | date:'mediumDate'}}</li>
</ul> </div>
</body>
<script src="../bower_components/angular/angular.min.js"></script>
<script>
var app = angular.module('myApp', []); app.filter('capitalize', function(){
return function(input){
if(input)
{
return input[0].toUpperCase() + input.slice(1);
}
}
}); app.controller('myController', ['$scope', function ($scope) {
$scope.team = [
{name:'Ethan',join:new Date('2014/07/28 00:00:00')},
{name:'jOhn',join:new Date('2010/02/14 00:00:00')},
{name:'tONY',join:new Date('2008/10/01 00:00:00')},
{name:'chaoYang',join:new Date('2015/08/01 00:00:00')},
{name:'hogan',join:new Date('2015/04/01 00:00:00')}]; $scope.join2015 = function(str){
return str.join.toString().indexOf('2015')>-1;
};
}]); </script>
</html>
在使用AngularJs的filter的时候,如果是简单的过滤,那么可以通过使用filter关键字,然后后面跟上简单的过滤条件就可以达成了。比如说我这里使用filter:{name:'Ethan'}条件,就可以把名称为Ethan的用户单独过滤出来了。
但是在很多场景中,简单的过滤并不是时时刻刻都能满足我们的需求的,这就需要我们使用自定义的过滤。在AngularJs中,自定义过滤条件一般说来有两种写法:一种是直接定义一个函数,接受input参数,然后通过return关键字返回过滤的结果。还有一种就是通过filter关键字定义,但是返回的是一个function,这个function其实是和直接定义过滤函数的效果是一样的。具体的使用方式可以参见上面的示例代码中的 join2015 过滤函数和 captialize 过滤函数,一个是过滤出2015年加入公司的新员工,另外一个是将当前的用户名称的首字母大写。
四,Directive模板项
在AngularJs中,一般说来都是不推荐直接操纵DOM元素的,但是在实际开发中,我们避免不了操纵DOM元素的场合,所以在AngularJs中,利用Directive可以实现这种效果。
说到Directive操纵DOM的方法,一般用ACME来表示: A代表Attribute(属性)替换法,C表示Class(样式)替换法,E表示Element(元素)替换法,M表示Memo(注释)替换法。我这里有张表,大家可以看看(参考):
字母 |
声明风格 |
示例 |
A |
属性 |
<div hello-world>attribute</div> |
E |
元素 |
<hello-world>element</hello-world> |
M |
注释 |
<!--directive:hello-world --> |
C |
样式 |
<div class="hello-world">class</div> |
来看个例子:
angular.module('myApp', [])
.controller('MyController',function($scope) {
$scope.team = [
{name:'ethan',join:new Date('2014/07/28 00:00:00')},
{name:'john',join:new Date('2010/02/14 00:00:00')},
{name:'tony',join:new Date('2008/10/01 00:00:00')},
{name:'chaoyang',join:new Date('2015/08/01 00:00:00')},
{name:'hogan',join:new Date('2015/04/01 00:00:00')}];
})
.directive('helloWorld',[function(){
return {
restrict: 'AEMC',
template: '<div>{{ text }}</div>',
replace: true,
controller: function($scope){
$scope.text = "Hello World!";
}
}
}])
在这个例子中,我主要是创建了一个包含AEMC的directive,那么在遇到上述列表中的4种示例的时候,都会输出<div>Hello World!</div>的结果。在这里需要注意的是,restrict的内容可以填写AEMC的任何一个字母或者多个,那么进行模板替换的时候,会自动根据字母的种类替换相应的模板,不存在的字母对应的模板将不会被自动替换。
在上面的例子中,你可以看到了一个controller,你可能会问,假如我要将外部数据传递入这个directive的话,该怎么办呢?
说道这个问题,我们就继续引申出下一个议题:传递外部数据到directive中的几种方式。
第一种方式,通过controller传递:在directive内部可以添加controller,然后将外部的scope传入,这样就可以实现数据共享。
第二种方式,通过link传递:在directive内部可以添加link,它附带三个很有用的参数:scope, element, attrs,其中scope由外部传入;element代表template种的元素;attrs则代表元素的属性集合。
具体使用方式,请看下面的例子:
.directive('hello', [function () {
return {
restrict: 'AEMC',
template: '<span ng-click="toggle()">Hello <strong>{{ person.name }}</strong></span>',
scope: true,
replace:true,
link : function(scope, element, attrs) {
scope.toggle = function toggle() {
alert('click me!'+scope.person.name);
console.log(scope.person);
}
}
};
}])
.directive('team', [function () {
return {
restrict: 'AEMC',
template: '<span ng-click="toggle()">Hello <strong>{{ name }}</strong></span>',
scope: {
name:"=forName"
},
replace:true,
link : function(scope, element, attrs) {
scope.toggle = function toggle() {
alert('click me!'+scope.name);
console.log(scope.name);
}
}
};
最后需要说一下在directive中的scope,也就是代码中的第5行所标示的scope。它主要来明确父类scope被directive共享的能力的。
假如scope设置为true,那么整个父类的scope都是可以被directive共享的,所以在第9行中,如果我们修改代码为:alert('click me!'+scope.person.join); 是可以跑出正确的结果的。
加入scope设置为name,并且后面追加了=forName,那么预示着我们传入directive的scope只能获取到父类scope中的person.name的值,无法再获取到其他的值,这样当我们把第25行代码修改为alert('click me!'+scope.join); 的时候,跑出的结果会是undefined。如果想跑出正确结果,请参见下面代码。
所有的代码附录如下:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="../bower_components/angular/angular.min.js"></script>
</head>
<body ng-controller='MyController'> <h1>hello world directive</h1> <hello-world>element</hello-world> <div hello-world>attribute</div> <div class="hello-world">class</div> <!--directive:hello-world --> <h1>Hello directive</h1>
<div ng-repeat='person in team'>
<hello ></hello>
</div> <h1>team directive</h1>
<div ng-repeat='person in team'>
<team for-name="person.name" for-join="person.join"></team>
</div> </body>
<script type="text/javascript">
angular.module('myApp', [])
.controller('MyController',function($scope) {
$scope.team = [
{name:'ethan',join:new Date('2014/07/28 00:00:00')},
{name:'john',join:new Date('2010/02/14 00:00:00')},
{name:'tony',join:new Date('2008/10/01 00:00:00')},
{name:'chaoyang',join:new Date('2015/08/01 00:00:00')},
{name:'hogan',join:new Date('2015/04/01 00:00:00')}];
})
.directive('helloWorld',[function(){
return {
restrict: 'AEMC',
template: '<div>{{ text }}</div>',
replace: true,
controller: function($scope){
$scope.text = "Hello World!";
}
}
}])
.directive('hello', [function () {
return {
restrict: 'AEMC',
template: '<span ng-click="toggle()">Hello <strong>{{ person.name }}</strong></span>',
scope: true,
replace:true,
link : function(scope, element, attrs) {
scope.toggle = function toggle() {
alert('click me!'+scope.person.name);
console.log(scope.person);
}
}
};
}])
.directive('team', [function () {
return {
restrict: 'AEMC',
template: '<span ng-click="toggle()">Hello <strong>{{ name }}</strong></span>',
scope: {
name:"=forName",
join:"=forJoin"
},
replace:true,
link : function(scope, element, attrs) {
scope.toggle = function toggle() {
alert('click me!'+scope.join);
console.log(scope.name);
}
}
};
}])
</script>
</html>
五,Service
在AngularJs中,controller经常会被销毁,当你的一个httprequest结束后,那么controller一般都会被销毁,所以你如果一直按F5刷新页面的话,那么controller也就会跟随你的按键,一遍一遍的创建,销毁,创建,销毁。所以如果你有数据需要在controller间共享,那么就需要涉及到Service了。利用service,我们可以提高代码的重用度,写出更加合理的代码来。
Angular 提供了3种方法来创建并注册我们自己的 service,他们分别是:
1. Factory
2. Service
3. Provider
用Factory,就是创建一个对象,然后为它添加各种属性,最后把这个对象返回。之后你就可以在Controller中注入Service,然后就可以尽情的使用了。
用Service,其实和Factory是一样的,但是不需要我们手动返回对象,它会自动返回,所以比Factory稍微简化了那么一点点。
用Provider,是唯一一种你可以传进 .config() 函数的 service。当你想要在 service 对象启用之前,先进行模块范围的配置,那就应该用 provider。
由于Service涉及的概念比较多,并且网上已经有一篇非常好的文章对此进行了概括,我就不在这里一一列举了。
这篇文章是:
英文版本:AngularJS 之 Factory vs Service vs Provider
中文版本:AngularJS 之 Factory vs Service vs Provider
这个系列断断续续的写了好几天,很多东西都是边看边写,终于把整个大体的框架给写出来了。当然,在实际使用中,还有很多细节我没有讲到的。如果后面发现有些小细节在实际开发中用的比较频繁的话,我也会慢慢的更新上来的。
AngularJs就讲解到这里,有什么问题,请留言,我会一一回复的。
前端见微知著AngularJS备忘篇:温故而知新,可以为师矣的更多相关文章
- AngularJS之备忘与诀窍
译自:<angularjs> 备忘与诀窍 目前为止,之前的章节已经覆盖了Angular所有功能结构中的大多数,包括指令,服务,控制器,资源以及其它内容.但是我们知道有时候仅仅阅读是不够的. ...
- 前端见微知著番外篇:Bitbucket进行代码管控
说道代码管控,一般都会提到TFS.Git等,但是在这里我们将要用到Bitbucket,其实其操作方式和Git基本上一样,但是和TFS则有很大的不同了.但是原理基本上都是一致的. 这里我不会过多的涉及到 ...
- ## 本篇文章对linux常用的一些命令做一下总结,如有需要补充以及不懂得地方,请在下方留言 适合于linux初学者,以及对命令掌握不牢的用来备忘
本篇文章对linux常用的一些命令做一下总结,如有需要补充以及不懂得地方,请在下方留言 适合于linux初学者,以及对命令掌握不牢的用来备忘一,磁盘管理1.显示当前目录位置 pwd2.切换目录 cd ...
- AngularJS资源合集[备忘]【申明:来源于网络】
AngularJS资源合集[备忘][申明:来源于网络] 地址:http://blog.csdn.net/allgis/article/details/44646597
- Angularjs ngTable使用备忘
项目中用到angularjs的表格ng-table,功能相当强大,像搜索.排序.checkbox.分页.每页表格显示数目等都有.API,demo什么的也只能参考官网了.这里做个备忘,哪天肯定还会用到. ...
- Chrome谷歌浏览器web前端开发好用插件(自己用)备忘
Chrome谷歌浏览器web前端开发好用插件(自己用)备忘 一.总结 英语好一点的话要什么工具就直接去Chrome插件里面找非常方便. 二.测试题-简答题 1.Chrome修改页面字符集是什么? 解答 ...
- Npm vs Yarn 之备忘大全
有则笑话,如此讲到:"老丈人爱吃核桃,昨天买了二斤陪妻子送去,老丈人年轻时练过武,用手一拍核桃就碎了,笑着对我说:你还用锤子,你看我用手就成.我嘴一抽,来了句:人和动物最大的区别就是人会使用 ...
- JavaScript 教程学习进度备忘(二)
备忘:之前,只将“JS 教程”学习完毕,这篇记录:“JS HTML DOM ”.“JS 对象”.“JS Window”.“JS 库” 书签:跳过:另外跳过的内容有待跟进 _______________ ...
- Sublime Text 备忘
Sublime Text已经被传成编程利器,那当然也是我们前端的利器了,刚开始用的时候,很多小问题,所以做个备忘,忘记的时候也可以翻出来看看,下次重装的时候可以用到. 1.设置自动换行 菜单栏 Vie ...
随机推荐
- 软工_Alpha阶段事后分析总计
1.设想和目标 1.1 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件主要解决狼人杀玩家在游戏时的一些痛点.因为之前自己对于游戏中那些不方便的地方有过体 ...
- 关于<appSettings file="app.config" >引用外部文件的配置值
web.config文件中,appSetting节点引用了外部的配置文件, <appSettings file="app.config"> </appSettin ...
- Eclipse下使用SVN版本控制
作者:朱先忠编译 转自天极[url]http://dev.yesky.com/356/2578856.shtml[/url] 简单介绍一些基本操作1.同步在Eclipse下,右击你要同步的工程-tea ...
- Aptana Studio 3 汉化简体中文版
最近开始学习ruby on rails了,同事推荐我用aptana这个编辑器,它对ror的支持比较好,所以安装了这个软件,但是发现都是英文的,所以在网上看汉化教程,幸亏有高手写过这个文章了,这里我只是 ...
- XML 在SQLServer中的使用
SQL Server对于XML支持的核心在于XML数据的格式,这种数据类型可以将XML的数据存储于数据库的对象中,比如variables, columns, and parameters.当你用XML ...
- 说一下output子句
Output子句日常灰常有用,而且用的地方也挺多,但是确好多时候被我们忽视,今天我就也简单扫盲一下这个语句的用法. Output子句 返回受 INSERT.UPDATE.DELETE 或 MERGE ...
- HTTP状态码206和416
HTTP 2xx范围内的状态码表明了:"客户端发送的请求已经被服务器接受并且被成功处理了". TTP/1.1 200 OK是HTTP请求成功后的标准响应 HTTP/1.1 206状 ...
- 在Myeclipse中将maven程序部署到tomcat中
用新版的m2e插件就可以了,而且发布后修改jsp是不需要重新发布的.MyEclipse10.6自带新版m2e,只需在Run Configuration的Maven Build中new一个输入confi ...
- Seq_file文件系统实例剖析
http://blog.chinaunix.net/uid-24432676-id-2607766.html 另 http://www.cnblogs.com/qq78292959/archive/2 ...
- 【JAVA 小结】Java关于类与对象的代码
分别建立2个类class works 和 Person import java.io.*; public class works { public static void main(String[] ...