AngularJS 是一款来自 Google 的前端 JavaScript 框架,也是 SPA(single-page-application,单页应用)框架。AngularJS 框架的体积非常小,但是设计理念和功能却非常强大,极大地简化前端开发的负担,它快速成为了 JavaScript 的主流框架,帮助开发者从事 web 开发。
SPA 和 MVC
SPA:单页面应用是指用户通过浏览器加载独立的 HTML 页面并且无需离开此导航页面。对用户操作来说,一旦加载和执行单个页面应用程序通常会有更多的响应,这就需要返回到后端 Web 服务器,而单页面应用为用户提供了一个更接近本地移动或桌面应用程序的体验,这就是单页面应用的优势所在。
MVC:模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC 被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
基本概念
针对客户端应用开发的 AngularJS 吸收了传统的 MVC 基本原则,但并不只是传统意义上的 MVC,更接近于 MVVM(Model-View-ViewModel)。
Model:是应用中的数据,一般是简单的 JavaScript 对象。
ViewModel:是一个用来提供特别数据和方法从而维护指定 View 的对象。
View:是 AngularJS 解析后渲染和绑定后生成的 HTML,它能帮助您创建 web 应用的架构。
$scope(作用域):是 AngularJS 中的一个 JavaScript 对象,这个对象使用简单的 API 来侦测和广播状态变化。在这里就是要将域模型暴露给视图(模板),只需把属性设置给$scope 实例,就可以在模板渲染时得到这个值。
$scope 也可以针对特定的视图来扩展数据和特定的功能,只要在$scope 实例上定义一些函数就能将特定的 UI 逻辑暴露给模板。
Controller(控制器):控制器的首要任务就是初始化$scope 对象。在实践中,初始化的逻辑由下面的这些责任组成:提供模型初始化的值;扩展$scope UI 行为(方法)。
控制器都是普通的 JavaScript 函数,它们并不必去继承一些框架特定提供的类,也不必去调用任何特定的 AngularJS API 才能去正确地执行它们的任务。
Directive(指令):指令可以用来创建自定义的标签,它们可以用来装饰元素或者操作 DOM 属性,也可以作为标签、属性、注释和类名使用。
使用 AngularJs 的几个理由
简单容易上手,将会由下个章节的几个例子来证明 AngularJs 的简单易用。
有谷歌团队支持的开源框架。
非常全面,涵盖了 Unit Test、HTML 模板、数据绑定等性质,是一个非常快速的前端开发解决方案。
实践教程
在 HTML page 里面加入 AngularJS
在您的 JavaScript 文件中加入以下代码,即可使用 AngularJS。
清单 1.加入 AngularJS 引用
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
从双向数据绑定开始
双向数据绑定是 AngularJS 的一个重要特性,它提供了一个简单的机制,能够让您通过{{ 表达式 }}这个形式轻松地绑定数据到 HTML 上,请看下面这个简单的数据绑定的例子。
清单 2.双向数据绑定
index.html!DOCTYPE html><html><head></head><body> <div ng-app=""> <input type="text" ng-model="data.message"> <h1>{{data.message + " world"}}</h1> </div> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script></body></html>
在最外层的 div 中设置了一个 ng-app 属性(少了这个属性,AngularJS 就没办法工作), 一般这个属性表明这个元素已经这个元素内部的所有元素都是属于设定的这个 app 的。在后面我们会详细说到 ng-app 这个属性。
在这个 div 内部,设置了一个 input,并对 data.message 这个 Model 做双向数据绑定。最后,使用{{data.message}}告诉 AngularJS 在指定的 HTML 处显示 data.message 这个 Model 的数据,也就是用户输入的数据。
就这么几句话,就已经是完成了一个本来要耗费多得多的时间和代码来开发的动态应用。在这里,我们不需要给双向数据绑定写任何规则,不需要给更新 Model 和 View 写任何代码,甚至不需要编写任何 Model。事实上,我们根本都还没开始碰 JavaScript。我们不需要写代码,直到我们想开发更个性化的应用行为为止。
controller 和$scope 的使用
controller 在 AngularJS 里面也是非常重要的,它把数据和前端显示内容联系在一起,这里将会提供一个例子演示 controller 和$scope 的基本用法。
清单 3.controller 和$scope
index.html<!DOCTYPE html><html><head> <script type="text/javascript" src="main.js"></script></head><body> <div ng-app=""> <div ng-controller="MyFirstCtrl"> <h1>{{data.message + " world"}}</h1> </div></div> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script></body></html>main.jsfunction MyFirstCtrl($scope){ $scope.data = {message: "Hello"};}
使用 ng-controller 这个属性替代上个例子中的 ng-model 属性。"MyFirstCtrl"这个 controller 的作用域是当前 div 以及其所有子元素。
在新建的 main.js 中,定义一个叫 FirstCtrl 的处理函数,传入$scope(这个$scope 关联于当前 div 元素及其所有子元素)。
现在我们可以在有 ng-controller= MyFirstCtrl 属性的 DOM 元素的任何子元素里访问这个 data 对象,因为它在$scope 上。
$scope 实际上就是一个 JavaScript 对象,controller 和 view 都可以访问它,所以我们能利用它在两者间传递信息。在这个$scope 对象里,我们既存储数据,又存储将要运行在 view 上的函数。
多个 controller 之间共享数据
本例将会使用 Service factory 来处理多个 controller 共享数据的问题
清单 4.Controller 之间的共享数据
index.html<!DOCTYPE html><html><head></head><body> <div ng-app="myApp"> <div ng-controller="MyFirstCtrl"> <input type="text" ng-model="data.message"> <h1>{{data.message}}</h1> </div> <div ng-controller="MySecondCtrl"> <input type="text" ng-model="data.message"> <h1>{{data.message}}</h1> </div> </div> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script> <script type="text/javascript" src="main.js"></script></body></html>main.jsvar myApp = angular.module('myApp', []);myApp.factory('Data', function() { return {message: "I'm data from a service"}})function FirstCtrl($scope, Data){ $scope.data = Data;}function SecondCtrl($scope, Data){ $scope.data = Data;}
现在我们定义第一个 app 命名为"myApp",它其实就是一个 AngularJS 的模块。[] 代表这个定义的模块没有任何依赖模块。注意,在 HTML 中的名字需要跟 JavaScript 中定义的 app 的名字一致。
有个这个 myApp,我们可以创建一个 factory 来提供我们需要的 service。在这个例子中,我们的 service 名字叫 Data,它返回了一个字符串"I'm Data from a service"。刷新页面,可以看到从 service 返回的数据。
图 1. 运行结果
创建一个 service 就是简单的返回一个函数,这个函数返回一个对象。这个对象是在创建应用实例的时候创建的(记住,这个对象是单例对象)。
从这个例子可以看到,两个 controller 之间是完全独立的,并没有 root controller 或者 controller 直接的继承关系,仅仅是把 service 和这个对象传入两个 controller 中。
使用 Filter
过滤器(filter)正如其名,作用就是接收一个输入,通过某个规则进行处理,然后返回处理后的结果。主要用在数据的格式化上,例如获取一个数组中的子集,对数组中的元素进行排序等。ng 内置了一些过滤器,它们是:currency(货币)、date(日期)、filter(子串匹配)、json(格式化 json 对象)、limitTo(限制个数)、lowercase(小写)、uppercase(大写)、number(数字)、orderBy(排序)。总共九种。下面例子是一个自定义的 filter。
清单 5. 使用 Filter
index.html<!DOCTYPE html><html><head></head><body> <div ng-app="myApp"> <div ng-controller="MyFirstCtrl"> <input type="text" ng-model="data.message"> <h1>{{data.message}}</h1> </div> <div ng-controller="MySecondCtrl"> <input type="text" ng-model="data.message"> <h1>{{data.message|reverse}}</h1> </div> </div> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script> <script type="text/javascript" src="main.js"></script></body></html>main.jsvar myApp = angular.module('myApp', []);myApp.factory('Data', function() { return {message: "I'm data from a service"}})myApp.filter('reverse', function(Data) { return function(text) { return text.split("").reverse().join("") + Data.message; }})function FirstCtrl($scope, Data){ $scope.data = Data;}function SecondCtrl($scope, Data){ $scope.data = Data; }}
在 main.js 中加入对 filter 的定义,我们定义了一个名为"reverse"的 filter,它的功能是把字符反转然后再加上从上一个清单中讲的 service 中得到的字符串。
我们对 MySecondCtrl 中的文字应用了"reverse"这个 filter,<h1>{{data.message|reverse}}</h1>, 所以在第二个 div 中的文字有了图中所示的效果。
图 2.运行结果
ng-repeat 指令属性
ng-repeat:在处理一组数据的时候,经常需要把同一个 UI 重复很多次,AngularJS 提供了 ng-repeat 属性来帮助您解决这个问题。
ng-repeat 指令遍历一个数据集合中的每个数据元素,加载 HTML 模版把数据渲染出来。被重复使用的模版元素,就是我们绑定了这个指令属性的 DOM 元素。每一个使用模版渲染的 DOM 元素都有自己的 scope。请看下面这个例子:
清单 6. 使用 ng-repeat
index.html<!DOCTYPE html><html><head></head><body> <div ng-app="myApp"> <div ng-controller="SecondCtrl"> <ul> <li ng-repeat="person in roommates" </li> </ul> </div> </div> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script> <script type="text/javascript" src="main.js"></script></body></html>main.jsvar myApp = angular.module('myApp', []);myApp.controller('SecondCtrl', function ($scope){ $scope.isRed = true; $scope.isGreen = false; $scope.roommates = [ { name: 'Lucy'}, { name: 'Lily'}, { name: 'Sean'}, { name: 'Adam'} ];});
在 HTML 中的 ul 标签上设置 ng-repeat 属性,并用"person in roomates"指明集合并做遍历,就可以加载 HTML 模板把集合中的元素都轻松渲染出来。结果如下:
图 3. 运行结果
ng-class 指令属性
ng-class 可以用来标记某个 class 是否被应用,通常用来处理是否加载某个 css 样式。请看下面这个例子。
更换之前例子中代码的 HTML Body 部分如下:在 HTML 中将每个 li 都设置上 ng-class 属性,且 ng-class="{'black':isBlack,'red':isRed}",意思是当 isBlack 的值为 true 时,加载 black class,相反 isBlack 的值为 false,不加载 black class。
清单 7. 使用 ng-class 例子的 HTML 和 CSS 部分
index.html<!DOCTYPE html><html><head> <title>AngularJS Tutorials</title> <link rel="stylesheet" type="text/css" href="Style.css" ></head><body> <div ng-app="myApp"> <div ng-controller="SecondCtrl"> <ul> <li ng-repeat="person in roommates" ng-class="{'black':isBlack,'red':isRed}">{{person.name}}</li> </ul> <button ng-click="changeColor()">Click Here</button> </div> </div> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script> <script type="text/javascript" src="main.js"></script> </body></html>Style.css.red { color: red;}.black { color: black;}
isBlack 的值为 true 还是 false,是在 JavaScript 里面决定的。如下所示,我们在初始化时给一个默认值,就是 isBlack 为 true,而 isRed 为 false。当点击了 button 之后触发 changeColor 函数,把 isBlack 的值设为!isBlack, 这样在每次点击 button 后 isBlack 的值都会发生改变,从而 html 上<li>的样式也跟着改变。
清单 8. 使用 ng-class 例子的 JS 部分
main.jsvar myApp = angular.module('myApp', []);myApp.controller('SecondCtrl', function ($scope){ $scope.isBlack = true; $scope.isRed = false; $scope.roommates = [ { name: 'Lucy'}, { name: 'Lily'}, { name: 'Sean'}, { name: 'Adam'} ]; $scope.changeColor = function () { $scope.isRed = !$scope.isRed; $scope.isBlack = !$scope.isBlack; }});
ng-click 指令属性
ng-click:ng-click 指令属性给 DOM 元素注册了一个点击事件的监听器。当此 DOM 元素上有点击事件发生(即当此 button 或 link 被点击时),AngularJS 就会执行表达式的内容,并相应地更新 view。ng-click 指令属性给 DOM 元素注册了一个点击事件的监听器。当此 DOM 元素上有点击事件发生(即当此 button 或 link 被点击时),AngularJS 就会执行表达式的内容,并相应地更新 view。在上面那个例子里,我们给 button 绑定了一个 changColor()的处理函数。 <button ng-click="changeColor()">Click Here</button>, 并且在 controller 里面实现了这个函数,这样在 click 这个 button 的时候就会调用对应的 changeColor 函数。
我们也可以用 ng-click 来调用在 controller 里写好并绑定在$scope 上的函数,例如:
清单 9. 使用 ng-click
index.html<button ng-click="sayHello()">Say hello</button>main.js app.controller('MyController', function($scope) { $scope.sayHello = function() { alert("hello!"); } });
ng-init 指令属性
ng-init:它其实是在启动时运行的函数,让我们能够在程序运行前设定初始变量的值。如下例所示,我们初始化了 name 为"tester"字符串。
清单 10. 使用 ng-init
<b ng-init='name = "tester"'>Hello, {{ name }}</b>
第一个 Directive——使用自定义指令属性
Directive 可以让我们创建自定义的指令属性,它可以让用户把自定义的行为绑定在某个 DOM 元素上,或者可以把这个 DOM 元素或其子元素变型。在这里例子中,我们创建了一个自定义指令属性 super。它的行为是返回的是一个 template,就是一个 html 代码段,把这个指令限制在"E",也就是元素。
清单 11. 使用自定义指令属性 Directive
index.html<!DOCTYPE html><html><head></head><body> <div ng-app="myApp"> <super></super> </div> <script type="text/javascript"src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><script type="text/javascript" src="main.js"></script></body></html>main.jsvar app = angular.module("myApp", []);app.directive("super", function() { return { restrict: "E", template: "<div>This is my first Directive </div>" }})
点开网页,可以看到从 Directive 返回的 HTML 段,因为我们设置了 restrict:"E",所以看到下图中 super 的子节点元素就是 Directive 返回的 div
图 4. 运行结果
限制您的 Directive——使用 Restrict
Restrict 用来限制只有满足条件的才可以使用自定义的指令属性,也就是 Directive。一共可以用于以下的四类,E-元素,A-属性,C-样式类,M-注释。下面是一个限制为 A(属性)的例子。
把上例中的<body>内的 div 替换成下面代码
清单 12. 在您的 Directive 上使用 Restrict
index.html<div ng-app="myApp"> <!-- directive:man --> <div super class="flash"></div></div>main.jsvar app = angular.module("myApp", []);app.directive("super", function() { return { restrict: "A", link: function() { alert("I'm working stronger") } }})app.directive("flash", function() { return { restrict: "C", link: function() { alert("I'm working faster") } }})app.directive("man", function() { return { restrict: "M", link: function() { alert("I'm comment") } }})
图 5. 运行结果
从 HTML 中可以看到 super 是 div 的用户自定义属性,flash 是一个样式类,<!-- directive:man --> 是一段注释,注释必须遵守这个格式。在 JavaScript 里面我们写了三个 directive,分别对应处理 link 后面对应的是处理函数。第一个 directive 限定的是 A(属性),第二个 directive 限定的是 C(样式类),第三个限定的是 M(注释),用对应的处理函数来处理。可以自己把 JavaScript 中的 restrict A 和 C,M 对调,会看到三个 alert 都不出现。
总结
我们已经看到了 AngularJS 最基本的用法以及一些简单的例子,还有很多 AngularJS 的精美特性没有提到,感兴趣的请阅读我提供的参考资源。