AngularJS 是一个用于开发动态 Web 应用的 JavaScript 框架。如果要开发一个 Web 应用,AngularJS 能够操作 HTML 使之动态地发生改变,而不是一个单纯的静态文档,它提供了像数据绑定和依赖注入 (DI) 这样的高级特定来简化应用的开发。
安装 bower
执行命令:
npm install -g bower
查看是否安装:
bower -v
安装 Grunt
sudo npm install -g grunt-cli
安装 Yeoman
在安装 Yeoman 之前,需要确认以下配置:
- Node.js 版本在0.10以上
- npm 版本在1.3.7以上
执行命令:
$ npm install --global yo
安装后,用下面这行命令进入 Yeoman 的菜单:
$ yo
结果显示如下:
安装 angular
执行命令:
$ npm install -g generator-angular
使用生成器搭建应用:
$ mkdir mytodo
$ cd mytodo
执行 yo
,选中 Run the Angular generator
,运行生成器。当你比较熟悉 Yo
的时候,就可以不通过菜单直接运行生成器:
$ yo angular
一些生成器也会提供一些有共同开发库 (common developer libraries)
的可选配置来定制你的应用,能够加速初始化你的开发环境。 generator-angular
会询问你需不需要使用 Sass
和或者Bootstrap
,使用 ’n'
和 ’y'
进行选择。
然后你需要选择你需要使用的Angular模块。Angular模块是一些带有特定功能的独立的JS文件。举个例子,ngResource模块(angular-resource.js)提供了RESTful服务。你可以使用空格键来取消项目。下面来看一看默认值。(当你在试用空格的效果时,确保所有的模块都被标记为绿色)
按下回车键。Yeoman 将会自动构建你的应用、拉取需要的依赖并在你的工作流中创建一些有帮助的Grunt任务(Grunt Tasks)。
由Yeoman构建的文件目录结构
打开 mytodo
目录,你会看到下面的文件结构:
浏览器中查看应用,要将 grunt server
运行起来:
grunt serve
运行命令后本地会启动一个基于 Node
的 http
服务。通过浏览器访问 http://localhost:9000
就可以看到自己的应用了。
现在可以打开编辑器开始更改应用。每次保存更改后,浏览器将会自动刷新,就是说你是不需要手动再刷新浏览器了。这个被称作live reloading,这提供了一个很好的方式来实时查看应用的状态。它是通过一系列的Grunt任务来监视你的文件的更改情况,一旦发现文件被改动了,live reloading就会自动刷新应用。在这个例子中,我们编辑了views/main.html,通过陈斌彬的技术博客我们从下面的状态:
编写AngularJS
- 创建新模板展现 Todo 列表
先将 views/main.html
中除了 class
是 jumbotron
的 div
以外的内容都删除,然后把jumbotron
替换成 container
<div class="container"></div>
更改 Angular
控制器模板(即scripts/controller/main.js
),将 awesomeThings
改为todos
:
'use strict';
angular.module('mytodoApp')
.controller('MainCtrl', function ($scope) {
$scope.todos = ['Item 1', 'Item 2', 'Item 3'];
});
然后更改视图 (views/main.html)
来显示我们的 Todo 事项:
<div class"container">
<h2>My todos</h2>
<p ng-repeat="todo in todos">
<input type="text" ng-model="todo">
</p>
</div>
在 p
标签中的 ng-repeat
属性是一个 Angular
指令 (directive)
,当获取到一个集合(collection)
中的项时,它将项实例化。在我们的例子中,你可以想象一下,每个 p
标签和它的内容都带着这个 ng-repeat
属性。对于每个在 todos
数组中的项,Angular
都会生成一组新的
<p><input></p>
ng-model
是另一个 Angular
指令,它主要是和 input
、select
、textarea
标签和一些自定义控件一起使用,达到数据双向绑定的效果。在我们的例子中,它用于显示一系列带有 todo
的值的文本输入域。 在浏览器中查看 ng-repeat
和 ng-model
动态变化的效果。在保存之前,我们的应用看起来应该是下图这个样子的:
- 添加一个 Todo 事项
我们将要实现添加新的 Todo 事项的功能。现在需要修改 views/main.html
:在 h2
元素和 p
元素之间加上一个 form
元素。现在你的 views/main.html
应该是下面这个样子:
<div class="container">
<h2>My todos</h2>
<!-- Todos input -->
<form role="form" ng-submit="addTodo()">
<div class="row">
<div class="input-group">
<input type="text" ng-model="todo" placeholder="What needs to be done?" class="form-control">
<span class="input-group-btn">
<input type="submit" value="Add" class="btn btn-primary">
</span>
</div>
</div>
</form>
<p></p>
<!-- Todos list -->
<p ng-repeat="todo in todos" class="form-group">
<input type="text" ng-model="todo" class="form-control">
</p>
</div>
这一步里我们在页面顶部增加了一个带有提交按钮的表单。这个表单使用了另一个 Angular
指令:ng-submit
。返回查看你的浏览器,现在的 UI
应该是下面这个这样子的:
如果现在点击 Add
按钮,什么事情都不会发生—— 我们接下来要实现添加的效果: ng-submit
是将一个 Angular
表达式绑定到表单的 onsubmit
事件上。如果 form
上没有绑定任何动作,它也会阻止浏览器的默认行为。在我们的例子中,我们添加了一个 addTodo()
表达式。 下面的 addTodo
方法是实现将新增的 Todo
事项添加入已有的事项列表中,然后清空顶部的文本输入域:
$scope.addTodo = function () {
$scope.todos.push($scope.todo);
$scope.todo = '';
};
将 addTodo()
方法加到 scripts/controllers/main.js
的 MainCtrl
控制器的定义中,现在你的控制器代码应该如下所示:
'use strict';
angular.module('mytodoApp')
.controller('MainCtrl', function ($scope) {
$scope.todos = ['Item 1', 'Item 2', 'Item 3'];
$scope.addTodo = function () {
$scope.todos.push($scope.todo);
$scope.todo = '';
};
});
再次在浏览器中查看,然后在顶部的输入框中输入新的 Todo 事项按下 Add
按钮。新增的这个事项就会立刻出现在你的 Todo 列表中!
- 移除一个 Todo 事项
添加一个移除事项的功能,先在列表中每一个 Todo 事项的的边上加上一个“移除”按钮。回到我们的视图模板 (views/main.html)
,在现有的 ng-repeat
指令上添加一个按钮。然后确认我们的输入框和移除按钮是对齐的,将 p
标签的 class
从 form-group
改成 input-group
。 在改动之前代码是这样的:
<!-- Todos list -->
<p ng-repeat="todo in todos" class="form-group">
<input type="text" ng-model="todo" class="form-control">
</p>
改动以后的代码:
<!-- Todos list -->
<p class="input-group" ng-repeat="todo in todos">
<input type="text" ng-model="todo" class="form-control">
<span class="input-group-btn">
<button class="btn btn-danger" ng-click="removeTodo($index)" aria-label="Remove">X</button>
</span>
</p>
在上面的代码中我们使用了一个新的 Angular
指令—— ng-click
。可以用 ng-click
来控制元素被点击时的行为。在这个例子中,我们调用了 removeTodo()
方法并将 $index
传入了这个方法。 $index
的值是当前 todo
项在整个 todo
数组中的位置的索引值。举个例子,数组中的第一项的索引值是0,那么0就会被传入 removeTodo()
;类似的,在一个五项的 Todo 列表中,最后一项的索引值是4,4就会被传入 removeTodo()
。 现在我们来实现这个 removeTodo()
方法,下面的代码是使用 JS
中的 splice
方法将要移除的项通过给定的 $index
值从数组中移除:
$scope.removeTodo = function (index) {
$scope.todos.splice(index, 1);
};
修改以后的控制器 (scripts/controller/main.js)
如下所示:
'use strict';
angular.module('mytodoApp')
.controller('MainCtrl', function ($scope) {
$scope.todos = ['Item 1', 'Item 2', 'Item 3'];
$scope.addTodo = function () {
$scope.todos.push($scope.todo);
$scope.todo = '';
};
$scope.removeTodo = function (index) {
$scope.todos.splice(index, 1);
};
});
现在你可以点击 ×
按钮将一个 Todo 事项从列表中移除。
现在虽然我们可以添加和移除 Todo
事项,但是这些记录都不能永久地保存。一旦页面被刷新了,更改的记录都会不见了,又恢复到我们 main.js
中设置的 todo
数组的值。不过不要担心这个问题,等我们了解更多关于使用 Bower
安装 package
了以后,这个问题就会被解决的。
使用 Bower 安装了一个 Angular 组件,叫做 AngularUI Sortable module
。用下面的指令我们可以检查现在已经安装上的 package:
$ bower list
为了确认有 AngularUI
的包可以使用,用Bower
查找 angular-ui-sortable
$ bower search angular-ui-sortable
搜索结果中只有一个和angular-ui-sortable有关。而且我们已经安装了 jQuery
,那么现在我们就一起把 jQuery UI
也一起安装上。为了节省搜索的时间,jQuery UI
的包的名字是 jquery-ui
。要一次性安装上它们使用下面的命令:
$ bower install --save angular-ui-sortable jquery-ui
使用 –save
更新 bower.json
文件中关于 angular-ui-sortable
和 jquery-ui
的依赖,这样你就不用手动去 bower.json
中更新依赖了。
看一下 bower_components
目录是不是所有包都已经拉下来了,你可以看到 jquery-ui
和 angular-ui-sortable
出现在之前已经安装的 Angular
包边上了:
1.让你的 Todo 应用可排序(使用 Angular Sortable
模块)这些新安装的依赖要被添加进我们的index.html
文件。你可以手动添加,不过其实Y eoman
会自动添加上。退出当前进程,再次运行
$ grunt serve
可以看到在 index.html
文件底部链接脚本的位置,jquery-ui/ui/jquery-ui.js
和 angular-ui-sortable/sortable.js
已经自动地被引入了。
为了使用 Sortable
模块,我们需要在 scripts/app.js
中更新 Angular
模块,将 Sortable
可以加载到我们的应用中,更改前代码如下所示:
angular
.module('mytodoApp', [
'ngAnimate',
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch'
])
将 ui.sortable
添加进数组中。现在 scripts/app.js
应该是下面这个样子:
angular
.module('mytodoApp', [
'ngAnimate',
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch',
'ui.sortable'
])
最后,在 main.html
中,我们需要将 ui-sortable
指令作为一个层将 ng-repeat
层包起来。
<!-- Todos list -->
<div ui-sortable ng-model="todos">
<p class="input-group" ng-repeat="todo in todos">
我们也需要添加一些内联的 CSS
,将鼠标显示为“可移动”样式来告诉用户这些 Todo 事项是可以移动的:
<p ng-repeat="todo in todos" style="padding:5px 10px; cursor: move;">
现在 Todo 列表的 HTML 部分应该是下面这样的:
<!-- Todos list -->
<div ui-sortable ng-model="todos">
<p class="input-group" ng-repeat="todo in todos" style="padding:5px 10px; cursor: move;">
<input type="text" ng-model="todo" class="form-control">
<span class="input-group-btn">
<button class="btn btn-danger" ng-click="removeTodo($index)" aria-label="Remove">X</button>
</span>
</p>
</div>
现在这个列表就是可以移动的了:
2.使用本地存储实现保存
Angular有一个模块叫做 angular-local-storage
可以很简便地帮我们实现本地存储,使用 Bower
安装:
$ bower install --save angular-local-storage
与我们添加 jQueryUI
和 AngularUI Sortable
的时候相似,我们需要将 angular-local-storage.js
引入 index.html
:
<script src="bower_components/angular-local-storage/angular-local-storage.js"></script>
因为我们是使用 bower.json
来索引我们的模块的,所以先用 Ctrl+c
退出当前的进程,然后再次输入 grunt serve
让 Yeoman
将新逻辑写入你的 index.html
,重新启动服务后现在你的 index.html
的脚本部分看起来应该是下面的样子:
将 localStorage
写入 scripts/app.js
中:
angular.module('mytodoApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'ui.sortable',
'LocalStorageModule'
])
在 app.js
中也要配置 localStorageServiceProvider
,将 todo
作为本地存储的前缀,这样你的应用就不会把其他应用中重名的变量的内容获取过来:
.config(['localStorageServiceProvider', function(localStorageServiceProvider){
localStorageServiceProvider.setPrefix('ls');
}])
我们的 scripts/app.js
现在应该是这样的:
'use strict';
angular.module('mytodoApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'ui.sortable',
'LocalStorageModule'
])
.config(['localStorageServiceProvider', function(localStorageServiceProvider){
localStorageServiceProvider.setPrefix('ls');
}])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.otherwise({
redirectTo: '/'
});
});
你也需要在你的控制器 (scripts/controllers/main.js)
中声明对本地存储服务的依赖。将 localStorageService
作为第二个传入参数添加到你的回调函数中。
'use strict';
angular.module('mytodoApp')
.controller('MainCtrl', function ($scope, localStorageService) {
// (code hidden here to save space)
});
那么现在呢,我们的 Todo 事项就不是从静态的数组中读取的,我们将会从本地存储里读取然后再将它们存入 $scope.todos
中。 我们还需要使用 Angular
的 $warch
监听器来监听 $scope.todos
的值得变化。如果有人添加或者删减了 Todo 项目,本地存储中的数据也会被同步, 因此,我们需要将现在的 $scope.todos
声明删掉:
$scope.todos = ['Item 1', 'Item 2', 'Item 3'];
替换成下面的代码:
var todosInStore = localStorageService.get('todos');
$scope.todos = todosInStore && todosInStore.split('\n') || [];
$scope.$watch('todos', function () {
localStorageService.add('todos', $scope.todos.join('\n'));
}, true);
现在我们的控制器是这样的:
'use strict';
angular.module('mytodoApp')
.controller('MainCtrl', function ($scope, localStorageService) {
var todosInStore = localStorageService.get('todos');
$scope.todos = todosInStore && todosInStore.split('\n') || [];
$scope.$watch('todos', function () {
localStorageService.add('todos', $scope.todos.join('\n'));
}, true);
$scope.addTodo = function () {
$scope.todos.push($scope.todo);
$scope.todo = '';
};
$scope.removeTodo = function (index) {
$scope.todos.splice(index, 1);
};
});
如果现在你在浏览器中查看应用,你会发现 Todo 列表中没有任何东西。因为这个应用从本地存储中读取了todo 数组,而本地存储中还没有任何 Todo 事项。
再来添加一些项目到列表中,当我们再次刷新我们的浏览器的时候,这些项目都还在。
小结
- 用 yo 搭建了一个应用的模板文件。
- 为了增添应用的功能,用 bower 安装应用需要的依赖。
- 用 grunt serve 搭建和预览我们的应用,所有的改动都能实时地反映在页面上,不需要手动刷新。