AngularJS是一个JavaScript框架,它通过指令扩展了HTML,且通过表达式绑定数据到 HTML。
顺便一提,什么是框架?比如struts2、spring、hibernate、thinkphp、wordpress等等。
那么,什么是组件?比如jdbc、jquery、swiper、layer、arttemplate等等。

一般来说,那些可复用的、用于简化开发工作的代码集合,大的叫框架,小的叫组件。
有人说jquery是框架?当然可以,大小并没有明确边界。
不要太纠结于概念,如无必要,勿增实体。

本文,主要学习归纳一下Angular的各种特性,包括双向数据绑定、定义应用和控制器、优化模板渲染延迟、自定义指令、作用域、HTTP请求获取数据、自定义服务、依赖注入、路由控制等。最后,会给出一个综合实例。

双向数据绑定

单向数据绑定的原理:模板+数据=>视图。

目前大多数前端框架都是单向数据绑定,比如jQueryUI、BackBone、Flex。

双向数据绑定原理:模板+数据=>视图,模板+视图=>数据。

Angular采用的,就是双向数据绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html ng-app>
<head>
<meta charset="UTF-8">
<title>双向数据绑定</title>
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
</head>
<body>
Hello {{'World'}}!<br/>
Your name: <input type="text" ng-model="yourname" placeholder="World">
<hr>
Hello {{yourname || 'World'}}!
</body>
</html>

定义应用和控制器

angular对象,是Angular的根对象。类似于express框架中的express对象,类似于seajs框架的seajs对象,类似于浏览器的window对象。
如果说angular对象是Angular中的班主任,那么应用(或者叫模块,app)就是Angular中的班长!而班主任不常出没,管事的就是班长。
控制器(controller),就是普通同学小明,负责控制Angular应用程序中的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!--app.html-->
<html ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>定义应用和控制器</title>
<style>
[ng:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
</style>
</head>
<body>
<div ng-controller="myCtrl">
名: <input type="text" ng-model="firstName"><br>
姓: <input type="text" ng-model="lastName"><br>
<br>
姓名: <span>{{firstName + " " + lastName}}</span><br>
姓名2: <span class="ng-cloak">{{fullName()}}</span><br>
姓名3: <span ng-bind="fullName()"></span>
</div>
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.firstName= "John";
$scope.lastName= "Doe";
$scope.fullName = function() {
return $scope.firstName + " " + $scope.lastName;
}
});
</script>
</body>
</html>

优化模板渲染延迟

在定义应用和控制器的例子中,我们看到,页面上先出现了表达式,之后才出现我们期望的结果。解决这个问题,常用的有两个办法。一个是使用ng-bind,另一个是添加ng-cloak样式。

自定义指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!--directive.html-->
<html>
<head>
<meta charset="UTF-8">
<title>自定义指令</title>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body ng-app="myApp">
<runoob-directive></runoob-directive>
<div runoob-directive></div>
<div class="runoob-directive"></div>
<!-- 指令: runoob-directive -->
<script>
var app = angular.module("myApp", []);
app.directive("runoobDirective", function() {
return {
//restrict : "A",
//restrict : "C",
//restrict : "M",
//replace : true,
template : "<h1>自定义指令!</h1>"
};
});
</script>
</body>
</html>

作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!--scope.html-->
<html ng-app="myApp">
<head>
<meta charset="utf-8">
<title>作用域</title>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body>
<div ng-controller="myCtrl">
<h1>姓氏为 {{lastname}} 家族成员:</h1>
<ul>
<li ng-repeat="x in names">{{x}} {{lastname}}</li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $rootScope) {
$scope.names = ["Emil", "Tobias", "Linus"];
$rootScope.lastname = "Refsnes";
});
</script>
<p>注意 $rootScope 在循环对象内外都可以访问。</p>
</body>
</html>

上面的例子中,$scope的作用域为myCtrl这个ng-controller的范围,$rootScope的作用域为myApp这个ng-app的范围。

HTTP请求获取数据

获取本地数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--http.html-->
<html>
<head>
<meta charset="utf-8">
<title>HTTP请求</title>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body ng-app="myApp" >
<div ng-controller="myCtrl">
<h1>欢迎你!{{username}}</h1>
</div>
<p> $http 服务向服务器请求信息,返回的值放入变量 "username" 中。</p>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http.get("http.json").then(function (response) {
$scope.username = response.data.username;
});
});
</script>
</body>
</html>

http.json中的内容为:

1
2
3
{
"username":"voidking"
}

需要注意的是,本例需要在服务器中访问。因为Angular的HTTP请求封装了XMLHttpRequest,而XMLHttpRequest的使用需要服务器环境。

获取服务器数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!--http2.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HTTP请求服务器数据</title>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body ng-app="myApp" >
<div ng-controller="myCtrl">
<h1>欢迎你!{{username}}</h1>
</div>
<p> $http 服务向服务器请求信息,返回的值放入变量 "username" 中。</p>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http, $httpParamSerializer) {
$http({
method:'POST',
url:'/angulardemo/http.php',
headers:{
'Content-Type':'application/x-www-form-urlencoded'
},
dataType: 'json',
data: $httpParamSerializer({username:'voidking'})
}).then(function (response) {
console.log(response.data);
$scope.username = response.data.username;
}, function errorCallback(response) {
console.log(response.data);
});;
});
</script>
</body>
</html>

新建http.php,内容如下:

1
2
3
4
5
6
7
8
9
<?php
$username = $_POST['username'];
$result = array(
'code' => '0',
'ext' => 'success',
'username' => $username
);
echo json_encode($result);
?>

自定义服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!--service.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>自定义Service</title>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body ng-app="myApp" >
<div ng-controller="myCtrl">
<p>自定义服务,用于转换16进制数:</p>
<p>255 的16进制是:</p>
<h1>{{hex}}</h1>
<hr>
<p>在获取数组 [255, 251, 200] 值时使用过滤器:</p>
<ul>
<li ng-repeat="x in counts">{{x | myFormat}}</li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.service('hexafy', function() {
this.myFunc = function (x) {
return x.toString(16);
}
});
app.controller('myCtrl', functio 大专栏  AngularJS入门篇n($scope, hexafy) {
$scope.hex = hexafy.myFunc(255);
$scope.counts = [255, 251, 200];
});
app.filter('myFormat',['hexafy', function(hexafy) {
return function(x) {
return hexafy.myFunc(x);
};
}]);
</script>
</body>
</html>

当创建了自定义服务,并连接到应用上后,我们可以在控制器,指令,过滤器或其他服务中使用它。

依赖注入

AngularJS 提供很好的依赖注入机制。什么是依赖注入?wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分。
该模式分离了客户端依赖本身行为的创建,这使得程序设计变得松耦合,并遵循了依赖反转和单一职责原则。与服务定位器模式形成直接对比的是,它允许客户端了解客户端如何使用该系统找到依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<!--di.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AngularJS依赖注入</title>
</head>
<body ng-app="mainApp" >
<h2>AngularJS 简单应用</h2>
<div ng-controller="CalcController">
<p>配置:{{constant}}</p>
<p>输入一个数字: <input type = "number" ng-model = "number" /></p>
<button ng-click = "square()">X<sup>2</sup></button>
<p>结果: {{result}}</p>
</div>
<hr>
<div ng-controller="CalcController2">
<p>再输入一个数字: <input type = "number" ng-model = "number" /></p>
<button ng-click = "square()">X<sup>2</sup></button>
<p>结果: {{result}}</p>
</div>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
<script>
var mainApp = angular.module("mainApp", []);
mainApp.config(function($provide) {
// 创建一个名叫MathService的provider
$provide.provider('MathService', function() {
this.$get = function() {
var factory = {};
factory.multiply = function(a, b) {
return a * b;
}
return factory;
};
});
});
// 创建一个名叫defaultInput的value
mainApp.value("defaultInput", 5);
// 创建一个名叫constant的constant value
mainApp.constant("constant", "constant value");
// 将MathService、defaultInput、constant注入到控制器
mainApp.controller('CalcController', function($scope, MathService, defaultInput, constant) {
$scope.number = defaultInput;
$scope.constant = constant;
$scope.result = MathService.multiply($scope.number,$scope.number);
$scope.square = function() {
$scope.result = MathService.multiply($scope.number,$scope.number);
}
});
/*--------以下是CalcController2的内容--------*/
// 创建一个名叫MathService2的factory
mainApp.factory('MathService2', function() {
var factory = {};
factory.multiply = function(a, b) {
return a * b;
}
return factory;
});
// 创建一个名叫CalcService2的service,并且注入MathService2
mainApp.service('CalcService2', function(MathService2){
this.square = function(a) {
return MathService2.multiply(a,a);
}
});
// 将CalcService2注入到控制器
mainApp.controller('CalcController2',function($scope,CalcService2){
$scope.number = 6;
$scope.result = CalcService2.square($scope.number);
$scope.square = function() {
$scope.result = CalcService2.square($scope.number,$scope.number);
}
});
</script>
</body>
</html>

provider()函数是用来创建provider对象的标准方法。

实际上,value()、constant()、factory()、service()全都是用来创建一个provider对象的方法,它们提供了一种方式来定义一个provider,而无需输入所有的复杂的代码。

路由控制

AngularJS 路由允许我们通过不同的 URL 访问不同的内容。
通过 AngularJS 可以实现多视图的单页Web应用(single page web application,SPA)。
通常我们的URL形式为http://runoob.com/first/page ,但在单页Web应用中AngularJS 通过 # + 标记 实现,例如:

1
2
3
http://runoob.com/#/first
http://runoob.com/#/second
http://runoob.com/#/third

当我们点击以上的任意一个链接时,向服务端请的地址都是一样的 (http://runoob.com/)。 因为 # 号之后的内容在向服务端请求时会被浏览器忽略掉。 所以我们就需要在客户端实现 # 号后面内容的功能实现。 AngularJS 路由 就通过 # + 标记 帮助我们区分不同的逻辑页面并将不同的页面绑定到对应的控制器上。

AngularJS 模块的 config 函数用于配置路由规则。通过使用 configAPI,我们请求把$routeProvider注入到我们的配置函数并且使用$routeProvider.whenAPI来定义我们的路由规则。
$routeProvider 为我们提供了 when(path,object) & otherwise(object) 函数按顺序定义所有路由,函数包含两个参数:
第一个参数是 URL 或者 URL 正则规则。
第二个参数是路由配置对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!--router.html-->
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>路由控制</title>
</head>
<body ng-app="ngRouteExample" class="ng-scope">
<div>
<div id="navigation">
<a href="#/home">Home</a>
<a href="#/about">About</a>
</div>
<div ng-view="">
</div>
<script type="text/ng-template" id="embedded.home.html">
<h1> Home </h1>
</script>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
<script src="http://apps.bdimg.com/libs/angular-route/1.3.13/angular-route.js"></script>
<script type="text/javascript">
angular.module('ngRouteExample', ['ngRoute'])
.controller('HomeController', function ($scope, $route) { $scope.$route = $route;})
.controller('AboutController', function ($scope, $route) { $scope.$route = $route;})
.config(function ($routeProvider) {
$routeProvider.
when('/home', {
templateUrl: 'embedded.home.html',
controller: 'HomeController'
}).
when('/about', {
templateUrl: 'about.html',
controller: 'AboutController'
}).
otherwise({
redirectTo: '/home'
});
});
</script>
</body>
</html>
1
2
<!--about.html-->
<h1> About </h1>

综合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!--complex.html-->
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>综合实例</title>
</head>
<body ng-app="ngRouteExample" class="ng-scope">
<div>
<div id="navigation">
<a href="#/page1">Page1</a>
<a href="#/page2">Page2</a>
</div>
<div ng-view="">
</div>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
<script src="http://apps.bdimg.com/libs/angular-route/1.3.13/angular-route.js"></script>
<script type="text/javascript">
var myApp = angular.module('ngRouteExample', ['ngRoute']);
myApp.controller('Page1Controller', function ($scope, $route) {
$scope.$route = $route;
$scope.content = '这是page1的内容';
});
myApp.controller('Page2Controller', function ($scope, $route) {
$scope.$route = $route;
$scope.content = '这是page2的内容';
})
myApp.config(function ($routeProvider) {
$routeProvider.
when('/page1', {
templateUrl: 'complex-page1.html',
controller: 'Page1Controller'
}).
when('/page2', {
templateUrl: 'complex-page2.html',
controller: 'Page2Controller'
}).
otherwise({
redirectTo: '/page1'
});
});
</script>
</body>
</html>
1
2
3
4
5
<!--complex-page1.html-->
<div id="page1" ng-controller="Page1Controller">
<h1>Page1</h1>
<p>{{content}}</p>
</div>
1
2
3
4
5
<!--complex-page2.html-->
<div id="page2" ng-controller="Page2Controller">
<h1>Page2</h1>
<p>{{content}}</p>
</div>

后记

至于输入验证、事件、动画、API等,本文不再讨论,用到时自行查阅文档。
本文完整源码地址:https://github.com/voidking/angulardemo

记录一个hexo的坑:如果文中出现了双括号,而且双括号没有被代码块包含,那么解析会报错,无法生成页面。

查找到的解决办法:

1
2
3
{% raw %}
内容
{% endraw %}

经测试,无效,就用汉字代替好了。

书签

AngularJS实战
http://www.imooc.com/learn/156

AngularJS 教程 | 菜鸟教程
http://www.runoob.com/angularjs/angularjs-tutorial.html

AngularJS中文网
http://www.apjs.net/

AngularJS中文社区
http://angularjs.cn/

图灵社区: 合集 : AngularJS入门教程
http://www.ituring.com.cn/minibook/303

AngularJS: API: API Reference
https://docs.angularjs.org/api

ngCloak
https://docs.angularjs.org/api/ng/directive/ngCloak

AngularJS : Why ng-bind is better than 双括号 in angular?
http://stackoverflow.com/questions/16125872/angularjs-why-ng-bind-is-better-than-in-angular

Metronic3.3网页模板在线演示
http://metronic.kp7.cn/

框架到底是个什么东西?
https://www.zhihu.com/question/32069908

理解AngularJS中的依赖注入
http://sentsin.com/web/663.html

Hexo的一个小BUG(Template render error)
http://www.jianshu.com/p/738ebe02029b

AngularJS入门篇的更多相关文章

  1. AngularJs入门篇-控制器的加深理解基础篇

    下面做的是一个更新时间的效果,每一秒钟就会更新一下,视图中会显示出当前的时间   下面的这个例子中,SceondController函数将接受两个参数,既该DOM元素的$scope和$timeout. ...

  2. angularJs入门篇-hello world 开头

    AngularJS 采用了完全不同的解决方案,它创建实时视图模板代替视图,而不是将数据合并进模板之后更新DOM. 任何一个独立视图组件中的值都是 动态替换的.这个功能可以说是AngularJS中最重要 ...

  3. AngularJS入门心得3——HTML的左右手指令

    在<AngularJS入门心得1——directive和controller如何通信>我们提到“AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文 ...

  4. AngularJS入门心得2——何为双向数据绑定

    前言:谁说Test工作比较轻松,最近在熟悉几个case,差点没疯.最近又是断断续续的看我的AngularJS,总觉得自己还是没有入门,可能是自己欠前端的东西太多了,看不了几行代码就有几个常用函数不熟悉 ...

  5. 《AngularJS入门与进阶》图书简介

    一.图书封面 二.图书CIP信息 图书在版编目(CIP)数据 AngularJS入门与进阶 / 江荣波著. – 北京 : 清华大学出版社, 2017 ISBN 978-7-302-46074-9 Ⅰ. ...

  6. (转载)从Java角度理解Angular之入门篇:npm, yarn, Angular CLI

    本系列从Java程序员的角度,带大家理解前端Angular框架. 本文是入门篇.笔者认为亲自动手写代码做实验,是最有效最扎实的学习途径,而搭建开发环境是学习一门新技术最需要先学会的技能,是入门的前提. ...

  7. AngularJS - 入门小Demo

    AngularJS四大特效 MVC模式.模块化设计.自动化双向数据绑定.依赖注入 如果了解了后端开发知识,想必对这些词汇不会陌生,AngularJS融合了后端开发的一些思想,虽然身为前端框架,但与jQ ...

  8. Membership三步曲之入门篇 - Membership基础示例

    Membership 三步曲之入门篇 - Membership基础示例 Membership三步曲之入门篇 -  Membership基础示例 Membership三步曲之进阶篇 -  深入剖析Pro ...

  9. spring boot(一):入门篇

    构建微服务:Spring boot 入门篇 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...

随机推荐

  1. Python程序中的线程操作(线程池)-concurrent模块

    目录 Python程序中的线程操作(线程池)-concurrent模块 一.Python标准模块--concurrent.futures 二.介绍 三.基本方法 四.ProcessPoolExecut ...

  2. Linux c 操作MySQL

    #include <mysql/mysql.h>#include <stdio.h>#include <stdlib.h>int main() { MYSQL *c ...

  3. kaggle注册获取数据

    安装谷歌访问助手,主要参考下面的作者写的 https://segmentfault.com/a/1190000020548973?utm_source=tag-newest 安装之后,打开蓝灯或其他翻 ...

  4. 对象数组和for循环遍历输出学生的信息

    public class Student { private int no; private String name; private int age; public Student(int no, ...

  5. 【DSP】TMS320F28335的GPIO

    --> 关于TMS320F28335的GPIO的基础操作 TI的c2000系列DSP大多数的外设信号与通用输入/输出 (GPIO) 信号复用. 这使得用户能够在外设信号或者功能不使用时将一个引脚 ...

  6. 关于JDBC、JdbcTemplate使用遇到的坑

    1.如果数据源是oracle(mysql结尾是可以使用";"的),sql字符串中结尾处禁止使用分号";",不然会报错:java.sql.SQLException ...

  7. 吴裕雄--天生自然python机器学习:K-近邻算法介绍

    k-近邻算法概述 简单地说,谷近邻算法采用测量不同特征值之间的距离方法进行分类. 优 点 :精度高.对异常值不敏感.无数据输入假定. 缺点:计算复杂度高.空间复杂度高. 适用数据范围:数值型和标称型. ...

  8. 微软不将《帝国时代》终极版上架Steam的原因到底是什么?

    毋庸置疑的是,<帝国时代>绝对是一款经典游戏.作为一款RTS名作,在过去的20年时间中<帝国时代>销量超过2000万部.数以千万计的玩家都沉溺于这款游戏中,<帝国时代&g ...

  9. python之time模块和hashlib模块

    一.time模块 import time print(time.strftime('%Y-%m-%d %H:%M:%S'))#获取当前的格式化时间,time.strftime(format) prin ...

  10. TreeviewEditor.rar

    本工具可以打开.保存指定格式的XML文件. 树形控件的节点可以编辑.删除.增加.使用本工具看方便地创建书或论文的目录大纲,我用这个工具已经写了好几本书了. 动态图1: 动态图2:编辑效果,支持节点拖曳 ...