前言
几个月之前了解过一点Angular,主要是通过phonecat应用程序了解一些入门东西,但是当被问及什么是Angular或者你对Angular的理解时,只记得一个MVVM双向数据绑定,显然这是不能令人满意的。现在重新来过吧。
ps:该文档只是见证自己学习Angular的过程。所用版本为1.4.3。另外向大家推荐个windows下各 API离线查找工具Velocity,官网:http://velocity.silverlakesoftware.com/,真的不是一般地好用。
AngularJS简介
AngularJS是一个为动态WEB应用设计的结构框架,创新点在于,利用数据绑定和依赖注入,使你不用再写大量的代码。AngularJS的一些出众之处在于:
- 构建一个CRUD应用可能用到的全部内容包括:数据绑定、基本模板标识符、表单验证、路由、深度链接、组件重用、依赖注入
- 测试方面包括:单元测试、端到端测试、模拟和自动化则是框架
- 具有目录布局和测试脚本的种子应用作为起点
而Angular信奉的是,当组建视图同时又要写软件逻辑时,声明式的代码会比命令式的代码好的多。
基本概念
首先举个例子:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>myApp</title>
<style>
#test {
width: 100px;
height: 100px;
background-color: red;
border: 1px solid #ccc;
}
</style>
<script src="js/jquery-1.7.1.js"></script>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="myCtrl">
<div id="test" ng-click="click()">Come ON!</div>
<p>{{width}} * {{height}}</p>
<p>Width: <input type="text" ng-model="width"/></p>
<p>Height: <input type="text" ng-model="height"/></p>
<script>
"use strict";
var myApp = angular.module("myApp", []);
myApp.controller(‘myCtrl‘, [‘$scope‘, function($scope) {
var oDiv = angular.element(‘#test‘);
$scope.width = oDiv.width();
$scope.height = oDiv.height();
$scope.click = function() {
$scope.width = parseInt($scope.width) + 10;
$scope.height = parseInt($scope.height) + 10;
};
$scope.$watch(‘width‘, function(to, from) {
oDiv.width(to);
});
$scope.$watch(‘height‘, function(to, from) {
oDiv.height(to);
});
}]);
</script>
</body>
</html>
从代码中可以看出,在HTML中引入了一些以ng开头的标记,这些就是angular(一下简称ng)声明式的代码。
- 使用ng-app声明ng的初始化工作,同时定义了ng应用的作用域,ng的初始化一般绑定在DOMContentLoaded事件中,可以使用
angular.bootstap(element,modules,config);
手动引导ng应用程序。 - ng-controller声明控制器,控制器里可以写相应的代码逻辑,修改相应的$scope,进行model到view的数据绑定工作。
- ng-click绑定click事件,事件在controller中声明,绑定到$scope.
- ng模板解析:默认使用
{{}}
- ng-model:ng的双向数据绑定声明
依赖注入
ng通过在函数参数中做手脚来完成“依赖声明”的功能。[‘$scope‘, ‘dep1‘, ‘dep2‘, function($scope, dep1, dep2) {}]
像AMD声明一样,该函数依赖于$scope,dep1,dep2,然后依次作为参数传递进去。也可以使用另外一种声明方式,利用ng中的函数隐藏属性$inject(不推荐):
var MyController = function($scope, greeter) {}
MyController.$inject = [‘$scope‘, ‘greeter‘];
someModule.controller(‘MyController‘, MyController);
在处理时,ng通过函数对象的toString()方法将该函数定义的代码转为字符串表现形式,然后利用正则表达式过滤出相应的参数,通过参数名获取资源,最后把资源作为参数调用定义的函数。
因此最好采用上述两种方法来显示声明所需的依赖,防止代码压缩过程中压缩相应的函数参数而报错。
作用域
每个ng应用都有一个根作用域($rootScope),有多个子作用域。因为一些指令会声明新的子作用域,这些作用域的结构关系和其绑定到相应的DOM结构是对应的。
属性查找:当ng查找某个模板中的某个数据时,会按照作用域链一直向上查找,直至找到或查至根作用域$rootrScope为至(独立作用域除外)。
<body ng-controller="parentCtrl">
<div ng-controller="childCtrl">
<p>hello {{name || "world"}}!</p>
</div>
</body>
事件冒泡:我们可以在scopes上模拟DOM事件类型的事件冒泡,该事件可以被广播到子作用域或者触发到父作用域。
<div ng-controller="EventController">
Root scope <tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="i in [1]" ng-controller="EventController">
<button ng-click="$emit(‘MyEvent‘)">$emit(‘MyEvent‘)</button>
<button ng-click="$broadcast(‘MyEvent‘)">$broadcast(‘MyEvent‘)</button>
<br>
Middle scope <tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="item in [1, 2]" ng-controller="EventController">
Leaf scope <tt>MyEvent</tt> count: {{count}}
</li>
</ul>
</li>
</ul>
</div>
<script>
angular.module(‘myApp‘, [])
.controller(‘EventController‘, [‘$scope‘, function($scope) {
$scope.count = 0;
$scope.$on(‘MyEvent‘, function() {
$scope.count++;
});
}]);
</script>
另外,我们还可以调用$watch()
检测某个属性改变。
模板与数据绑定
ng中的主要特点之一就是双向数据绑定。
数据->模板:
可以直接使用ng的默认模板标记{{name}}
,则可以直接绑定一个作用域内的变量。当然也可以更改使用自定义的标记//name//
<script>
myApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol(‘//‘);
$interpolateProvider.endSymbol(‘//‘);
});
</script>
模板:
ng有自己的一套强大的模板命令:
1.ng-include直接引入模板内容
<div ng-include="‘template.html‘"></div>
ng-include内容必须是字符串,需多加个引号。
template.html主要有两种定义方式:
(1)script标签中定义:type属性值为“text/ng-template”,id为ng-include属性值
<script type="text/ng-template" id="template.html">
<p>This is the content of the template</p>
</script>
(2)外部html文件中定义:该html文件名为ng-include的属性值
Content of template.html
2.内容控制:
(1)ng-cloak
该指令绑定内容不显示,直到ng解析相关指令后才显示解析后的内容,避免相关内容解析前后出现跳转现象。
(2)ng-show/ng-hide
根据表达式的值来改变绑定该命令的DOM的display属性,相关CSS类已经在ng文件中提前定义,且使用了!important
提升权重。
点我: <input type="checkbox" ng-model="checked">
<div>
显示:
<div ng-show="checked">
选中时显示。
</div>
</div>
<div>
隐藏:
<div ng-hide="checked">
选中时隐藏。
</div>
</div>
(3)ng-if
不同于ng-show改变DOM的display属性来显示隐藏节点,ng-if根据表达式的布尔值判断:false则从文档中移除该DOM节点,true则向文档中添加该DOM节点。
(4)ng-switch
根据值的匹配情况来显示相应的节点:
ng-init直接在模板中进行赋值,与作用域无关。
<div ng-init="a=1">
<div ng-switch on="a">
<div ng-switch-when="1">1</div>
<div ng-switch-when="2">2</div>
<div ng-switch-default>other</div>
</div>
</div>
(5)ng-repeat
遍历对象或数组:
<div ng-init="list = [{name: ‘AAA‘}, {name: ‘BBB‘}, {name: ‘CCC‘}]">
<ul ng-repeat="member in list">
<li>{{$index}}</li>
<li>{{$first}}</li>
<li>{{$middle}}</li>
<li>{{$last}}</li>
<li>{{$odd}}</li>
<li>{{$even}}</li>
<li>{{member.name}}</li>
</ul>
</div>
额外变量:
$index 当前索引
$first 是否为首元素
$middle 是否为中间元素
$last 是否为尾元素
$odd 当前索引是否为奇数
$even 当前索引是否为偶数
遍历对象使用(key, value) in obj.
另外,默认待遍历的数组中不能有重复值,因为数组元素与相应的DOM元素是一对一的关系。如需要使用:
<ul ng-repeat="n in [10,10,10,10] track by $index">
{{n}}
</ul>
(6)其它
- ng-src src属性
- ng-href href属性
- ng-checked 选中状态
- ng-selected 被选择状态
- ng-disabled 禁用状态
- ng-multiple 多选状态
- ng-readonly 只读状态
3.样式
(1)ng-style
使用对象字面量的形式来赋值:
<input type="button" value="set color" ng-click="myStyle={color:‘red‘}">
<input type="button" value="set background" ng-click="myStyle={‘background-color‘:‘blue‘}">
<input type="button" value="clear" ng-click="myStyle={}">
<br/>
<span ng-style="myStyle">Sample Text</span>
<pre>myStyle={{myStyle}}</pre>
(2)ng-class
直接通过字符串绑定CSS中预定义的类
<!doctype html>
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="js/angular.min.js"></script>
<style>
.strike {
text-decoration: line-through;
}
.bold {
font-weight: bold;
}
.red {
color: red;
}
</style>
</head>
<body>
<p ng-class="style">Using String Syntax</p>
<input type="text" ng-model="style"
placeholder="Type: bold strike red" aria-label="Type: bold strike red">
</body>
</html>
4.事件绑定:
模板中的事件绑定预定义了一些常用的事件绑定指令,可以直接传递相关的处理程序且直观地在DOM中声明。
- ng-blur
- ng-change
- ng-click
- ng-dblclick
- ng-focus
- ng-keydown
- ng-keypress
- ng-keyup
- ng-mousedown
- ng-mouseenter
- ng-mouseleave
- ng-mousemove
- ng-mouseover
- ng-mouseup
使用$event可以给相应的事件处理函数传递事件对象本身
<input ng-keyup="show($event)">
<p>event keyCode: {{ keyCode }}</p>
<p>event altKey: {{ altKey }}</p>
<script>
angular.module(‘myApp‘,[])
.controller(‘myCtrl‘, function($scope) {
$scope.show = function($event) {
$scope.keyCode = $event.keyCode;
$scope.altKey = $event.altKey;
}
});
</script>
5.表单
HTML中form是一个核心控件,是网页与用户进行交流的主要方式之一。而ng对form进行了封装”ng-form”,区别是HTML中的form不能嵌套,而ng-form可以嵌套。而ng-form的目的就是为了统一控制,而不是为了取代form标签。
form中的一些控件可以预先通过ng-controller中的$scope绑定一些状态。
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="js/angular.min.js"></script>
</head>
<script>
angular.module(‘myApp‘, [])
.controller(‘FormController‘, [‘$scope‘, function($scope) {
$scope.userType = ‘guest‘;
}]);
</script>
<style>
.my-form {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
background: transparent;
}
.my-form.ng-invalid {
background: red;
}
</style>
<body>
<form name="myForm" ng-controller="FormController" class="my-form">
userType: <input name="input" ng-model="userType" required >
<span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
age: <input name="input_b" ng-model="age" required pattern="\d+">
<span class="error" ng-show="myForm.input_b.$error.required">Required!</span>
<span class="error" ng-show="myForm.input_b.$error.pattern">Pattern!</span><br>
<code>userType = {{userType}}</code><br>
<code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
<code>myForm.input.$error = {{myForm.input.$error}}</code><br>
<code>myForm.$valid = {{myForm.$valid}}</code><br>
<code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
</form>
</body>
</html>
ng给form提前定义了一些CSS类:
- ng-valid 当表单验证全部通过时使用
- ng-invalid 表单中有未通过验证的控件时使用
- ng-pristine 当表单为被修改之前使用
- ng-dirty 当表单被修改之后使用
- ng-submitted 当表单被提交之后使用
form对象有一些属性:
- $pristine 表单是否未被动过
- $dirty 表单是否被动过
- $valid 表单是否通过验证
- $invalid 表单是否未通过验证
- $error 表单的错误对象
其中$error是一个hash对象,引用表单控件中未通过验证的键值对。属性是验证失败信息,值是对应的实例列表。另外,该失败对象是按一定的验证逻辑所取,例如上例中的age输入框,先判定required,在判定pattern。$error对象可能的属性有:email/max/maxlength/min/minlength/number/pattern/required/url/date/time/week/month.
同时,我们也能在form中的具体某个input框中查看相应的错误,格式为formName.inputName.$error
.但是,input控件的相关属性是ng-required/ng-pattern等,经测试发现,仅ng-maxlength,ng-max,ng-min与HTML5中相关属性有区别:ng-maxlength=’12’可以输入超过12个字符,但超过相关属性为true,而maxlength则最多只能输入12个字符,超过默认丢弃,相关错误属性永不为true.
(1)checkbox/radio
可以为checkbox分别绑定ng-true-value/ng-false-value的值,为radio绑定value值。而这些值与视图中的相关控件是否选中相对应。
checkbox: <input type="checkbox" name="box" ng-model="check" ng-true-value="‘China‘" ng-false-value="‘Beijing‘" />
<span>check = {{check}}</span>
<hr />
radio: <input type="radio" name="checkRadio" ng-model="checkRadio" value="China" />
radio: <input type="radio" name="checkRadio" ng-model="checkRadio" value="Beijing" /><br>
<span>radio = {{checkRadio}}</span>
(2)textarea
包含input框中相应属性,仅多了ng-trim指令。
(3)select
select控件中,有个用于呈现下拉选项的指令ng-options
主要用于select模板中绑定的是非字符串。主要用法见下例:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<div ng-controller="myCtrl">
<h3>参数是数组情况下:</h3>
<label>Color (可以指定value为null的option)
<select ng-model="myColor1" ng-options="color.name for color in colors">
<option value="">——Choose Color——</option>
</select>
<span>选择颜色:{{myColor1}}</span>
</label><br />
<label>Color(区分显示和值)
<select ng-model="myColor2" ng-options="color.name as color.shade for color in colors">
</select>
<span>选择颜色:{{myColor2}}</span>
</label><br />
<label>Color(以shade值分组)
<select ng-model="myColor3" ng-options="color.name group by color.shade for color in colors">
</select>
<span>选择颜色:{{myColor3}}</span>
</label><br />
<label>Color (以shade分组,且有附加条件)
<select ng-model="myColor4" ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
</select>
<span>选择颜色:{{myColor4}}</span>
</label>
<h3>参数是对象情况下:</h3>
<label>Color (以shade分组,且有附加条件)
<select ng-model="ordinalNumber1" ng-options="value.name for (key, value) in ordinal">
</select>
<span>所选列:{{ordinalNumber1}}</span>
</label><br />
<label>Color(区分显示和值)
<select ng-model="ordinalNumber2" ng-options="value.value as value.name for (key, value) in ordinal">
</select>
<span>所选列:{{ordinalNumber2}}</span>
</label><br />
<label>Color(以shade值分组)
<select ng-model="ordinalNumber3" ng-options="value.name group by value.group for (key, value) in ordinal">
</select>
<span>所选列:{{ordinalNumber3}}</span>
</label><br />
<label>Color (以shade分组,且有附加条件)
<select ng-model="ordinalNumber4" ng-options="value.value group by value.group disable when value.notAnOption for (key, value) in ordinal">
</select>
<span>所选列:{{ordinalNumber4}}</span>
</label>
</div>
<script>
angular.module(‘myApp‘, []).controller(‘myCtrl‘, [‘$scope‘, function($scope) {
$scope.colors = [
{name:‘black‘, shade:‘dark‘},
{name:‘white‘, shade:‘light‘, notAnOption: true},
{name:‘red‘, shade:‘dark‘},
{name:‘blue‘, shade:‘dark‘, notAnOption: true},
{name:‘yellow‘, shade:‘light‘, notAnOption: false}
];
$scope.ordinal = {
first: {
name: "1st",
value: "NO.1",
group: "1-3"
},
second: {
name: "2nd",
value: "NO.2",
group: "1-3",
notAnOption: true
},
third: {
name: "3nd",
value: "NO.3",
group: "1-3",
notAnOption: true
},
fourth: {
name: "4th",
value: "NO.4",
group: "4-5"
},
fifth: {
name: "5th",
value: "NO.5",
group: "4-5",
notAnOption: false
}
};
}]);
</script>
</body>
</html>
通常情况下,ng-repeat也可用在option元素中,但ng-repeat会为每个遍历的实例创造一个新的作用域,而使用ng-options则具有节省内存,响应更快捷的优点。
模板->数据:
ng中模板到数据的绑定主要是通过ng-model来实现。
<p>hello {{name || "World"}}!</p>
<hr/>
<input type="text" ng-model="name"/>
当我们在input框中输入内容,会发现模板中内容也相应改变。实际上,ng中的双向数据绑定主要是通过ng-model来实现。
过滤
ng默认提供了一些可以直接使用的过滤指令
- currency 货币过滤指令
- date 时间过滤指令
- filter 数组过滤指令
- json 将js对象转换为json字符串
- limitTo 截取字符串/数组/数字的一部分
- lowercase 字符串转小写
- number 格式化数字
- orderBy 数组排序
- uppercase 字符串转大写
- linky 找出文本输入中的连接然后格式化
模板中使用
我们可以在模板中使用过滤命令,使用方法类似与linux系统中的管道命令{{expression | filter1:arg1:arg2:... | filter2:arg}}
即我们可以在一个语句后面使用多个过滤命令,依次用”|”分割开即可,也可以在一个过滤命令后面传递多个参数,依次用“:”分割开即可。
<span>{{1437272917693 | date:"MM/dd/yyyy ‘at‘ h:mma"}}</span><br>
<span>{{ ["AAAAA","AAAA","AAA","AA","A","BBB","BB"] | filter:‘A‘ | limitTo: 2 }}</span><br>
JS中使用
JS中使用过滤命令有两种方式:
(1)<filterName>Filter
方式:
在依赖声明中明确声明需要使用filterName的过滤指令,然后就可以在内部向该指令函数传递参数,返回处理后的数据:
angular.module(‘myApp‘, [])
.controller(‘filterCtrl‘, [‘filterFilter‘, ‘dateFilter‘, ‘numberFilter‘, ‘currencyFilter‘, ‘$scope‘,
function(myFilter, dateFilter, numberFilter, currencyFilter, $scope) {
$scope.array = contacts;
$scope.filteredArray = myFilter(this.array, ‘a‘);
$scope.date = dateFilter(1437272917693, ‘mediumDate‘);
$scope.number = numberFilter(1437272917693,2);
$scope.money = currencyFilter(100.1234,‘¥‘,2);
}]);
(2)$filter方式
依赖声明中引入$filter,然后我们就可以在内部向该函数传入我们想要使用的过滤指令名,返回对应的过滤指令函数。较方式1简单:
angular.module(‘myApp‘, [])
.controller(‘myCtrl‘, [‘$scope‘, ‘$filter‘, function($scope, $filter) {
$scope.filteredText = $filter(‘uppercase‘)(‘China‘);
$scope.number = $filter(‘number‘)(123456789.1123);
}]);
自定义过滤命令
如果感觉ng默认提供的几种过滤指令满足不了需求,我们可以自定义指令。通过你的module中的filter工厂函数就可以自定义一个新的指令。
angular.module(‘myApp‘, [])
.filter(‘reverse‘, function() {
return function(input, uppercase) {
input = input || ‘‘;
var out = "";
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
}
// 可传入参数uppercase
if (uppercase) {
out = out.toUpperCase();
}
return out;
};
})
向filter工厂函数传入命令名,初始化函数,返回一个过滤函数,过滤函数第一个参数是待过滤的内容,其余参数依次输入。仅当该过滤指令的输入内容改变时,ng才会执行该指令。
另外,自定义的过滤函数应该全部是无状态的,那些有状态的指令无法被ng优化,经常导致表现问题。而如果真的需要维持状态的过滤命令,可指定该过滤函数的$stateful属性,这样每次模板中内容改变该过滤函数都会执行一次。
angular.module(‘myStatefulFilterApp‘, [])
.filter(‘decorate‘, [‘decoration‘, function(decoration) {
function decorateFilter(input) {
return decoration.symbol + input + decoration.symbol;
}
decorateFilter.$stateful = true;
return decorateFilter;
}])
另外,自定义的过滤函数只能在模板中使用。
AJAX
与其它框架一样,ng也提供了类似的一套AJAX封装,内部通过XMLHttpRequest对象或JSONP方式。
$HTTP
$HTTP提供基本的操作服务,传入一个config配置对象,设置一些参数,返回一个可以注册成功、失败回调函数的promise对象。$http常用的配置有:
- method 请求方法
- url 请求路径
- params GET请求的参数
- data 请求报文(POST请求的参数)
- headers 定义请求报头
- cache GET请求的缓存
- timeout 过期时间
- responseType 响应类型
另外对于一些请求方式,有一些简写:
- $http.get(url, [config])
- $http.delete(url, [config])
- $http.head(url, [config])
- $http.jsonp(url, [config])
- $http.post(url, data, [config])
- $http.put(url, data, [config])
$http({method: $scope.method, url: $scope.url}).
success(function(data, status) {
$scope.status = status;
$scope.data = data;
}).
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
$http属性:
- pendingRequests 当前请求队列状态,主要用于测试
- defaults 请求的全局配置
异步回调
ng的异步回调函数服务$q为AJAX的异步回调提供服务。
(1)$q用法
- $q(resolveFn, errorFn) 注册并返回一个promise对象
- $q.defer() 返回一个deferred实例
- $q.reject(reason) 包装一个错误
- $q.when(value) 返回一个promise对象
- $q.resolve(value) 与when方法一样,为了与ES6保持一致性
- $q.all(promises) 将多个promise对象合并成一个promise对象
(2)deferred对象:通过$q.defer()构建
- resolve(value) 成功回调
- reject(reason) 失败回调
- notify(value) 更新promise的执行状态
- promise属性 返回一个promise对象
(3)promise
- then(successCallback, errorCallback, notifyCallback) 分别注册成功,失败,通知的回调函数
- catch(errorCallback) 相当于then(null, errorCallback) 注册失败回调函数
- finally(callback, notifyCallback)
angular.module(‘myApp‘, []).controller(‘myCtrl‘, [‘$q‘, function($q) {
function success(message) {
console.log("OK! " + message);
}
function error(message) {
console.log("error! " + message);
}
function notify(message) {
console.log(‘notify! ‘ + message);
}
var defer = $q.defer(),
promise = defer.promise;
promise.then(success, error, notify);
// defer.resolve(‘resolve‘);
// defer.reject(‘reject‘);
defer.notify(‘notify‘);
其它部分,明天继续。。。
版权声明:本文为博主原创文章,未经博主允许不得转载。