Angular使用了应该有一个月了,一直想总结来着,但是比较懒,所以一直拖着。这次正好趁着分享,写一篇相关内容的文章,来总结下Angular的基本使用和常用指令。本来打算写的是入门教程,发现入门教程怎么写都感觉不如官网,所以放弃了,可以把这一篇当做入门教程的补充版。如果大家要看入门教程可以看官方的angular tutorial,中文版的话1.5版本的没找到,大家可以看angular 1.4angular 教程

这里以一个简单的图片列表应用为demo,查看效果可以点击:http://www.liuchungui.com/example/angularExample/01/查看,github地址是angularExample 01

现在跟着我一步一步来吧!

一、使用npm管理器搭建环境

我们使用npm包管理器来搭建环境,因为它易于安装和管理。我们先创建一个文件夹,初始化工程。

1
2
3
$ mkdir angularExample
$ cd angularExample
$ npm init

初始化的时候,会让你填入项目的一些信息,之后我们输入yes之后,会在当前目录下生成一个package.json,里面是我们填入的信息。

之后,我们安装angularJSjquerybootstrap

1
2
3
4
5
6
#安装angular
$ npm install --save angular
#安装jquery
$ npm install --save jquery
#安装bootstrap
$ npm install --save bootstrap

这时,我们的项目工程文件下多了个node_modules文件夹,node_modules下有angularjquery,这就说明我们安装成功了。

查看package.json,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"name": "learning",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"angular": "^1.5.8",
"bootstrap": "^3.3.7",
"jquery": "^3.1.1"
}
}

看到dependencies那一块,这里说明我们项目依赖的是angular 1.5.8jquery 3.1.1bootstrap 3.3.7

在安装完依赖之后,我们需要将我们的工程放入一个web环境下,例如apache等等。如果你没有对应的web环境,我们可以使用npm快速搭建一个,这里可参考快速搭建静态服务器的几种方法

到此,我们的环境算是搭建好了,现在开启我们的旅程吧!

二、图片列表应用

1、导入文件

克隆或下载angularExample,将工程下面的demoApp.css样式文件、image文件夹和resource文件夹copy到你的工程目录下,这是我们所需要样式、图片和资源。

创建index.htmldemoApp.js,在index.html文件中,导入angularjquerybootstrap这三个框架,然后导入demoApp.cssdemoApp.js

下面是index.html文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="demoApp.css">
<script src="node_modules/angular/angular.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script src="demoApp.js"></script>
</head>
<body>
</body>
</html>

2、添加列表展示页面

下面是添加列表展示页之后index.html的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en" ng-app="demoApp">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="demoApp.css">
<script src="node_modules/angular/angular.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script src="demoApp.js"></script>
</head>
<body ng-controller="demoController">
<div class="photos">
<div class="photoItem" ng-repeat="info in imageList">
<img ng-src="{{info.imageUrl}}" ng-style="imageStyle(info.width)"/>
</div>
</div>
</body>
</html>

demoApp.js中的内容:

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
'use strict';
var demoApp = angular.module('demoApp', []);
demoApp.controller('demoController', function ($scope, $http) {
/**
* 获取imageList.json文件中的json数据
*/
$http.get('resource/imageList.json').success(function (data) {
console.log(data);
//绑定到scope当中的图片列表中
$scope.imageList = data;
}).error(function (error) {
console.log(error);
});
/**
* 设置图片的宽和高
*/
$scope.imageStyle = function(width) {
var windowWidth = window.document.body.clientWidth;
var scale = windowWidth/1932;
return {
'height': parseInt(352*scale) + 'px',
'width': parseInt((windowWidth-36)/4) + 'px'
};
};
/**
* jquery事件
* resize是当窗口变化时,会触发resize事件
*/
$(window).resize(function(){
/**
* angular 事件, 重新更新界面, 做手动刷新
*/
$scope.$apply(function(){
//do something to update current scope based on the new innerWidth and let angular update the view.
});
});
});

上面的代码,我们实现了一个简单展示图片的列表,而且做到了响应式布局,你可以在浏览器里面看到效果。这是怎么做到的?

首先,创建demoApp应用

我们在index.html文件中<html lang="en" ng-app="demoApp">中使用了ng-app指令,这个指令指定了angulardemoApp模块作为它的根模块,也指定了html标签作为我们angular应用的作用域。

随后,我们在demoApp.js文件中注册了demoApp模块,名称与上一步ng-app指定的名称对应,注册模块的模式是固定的,如下:

1
2
3
4
5
6
7
'use strict';
/**
* 注册模块, []是依赖的模块
* @type {angular.Module}
*/
var demoApp = angular.module("demoApp", []);

所使用的angular.module中有两个参数,第一个参数是注册的模块名称,第二个参数是这个注册模块依赖的模块。

然后、创建控制器

创建控制器,在html中只需要使用ng-controller指定一下就行了,如<body ng-controller="demoController">,那这个body标签就成为控制器的作用域了。而在js中,需要创建一个对应的控制器,样式如下面:

1
2
demoApp.controller("demoController", function ($scope, $http) {
});

最后,创建图片列表

图片列表中模块index.html中的代码如下:

1
2
3
4
5
<div class="photos">
<div class="photoItem" ng-repeat="info in imageList">
<img ng-src="{{info.imageUrl}}" ng-style="imageStyle(info.width)" />
</div>
</div>

它首先使用ng-repeat指令,遍历了imageList的内容,然后实例化了多个<img>
在说ng-repeat命令之前,我们先来看看这个imageList是怎么来的。我们在demoApp.js中看到下面一段代码:

1
2
3
4
5
6
7
8
9
10
/**
* 获取imageList.json文件中的json数据
*/
$http.get('resource/imageList.json').success(function (data) {
console.log(data);
//绑定到scope当中的图片列表中
$scope.imageList = data;
}).error(function (error) {
console.log(error);
});

这段代码注释很清楚,就是我们通过$http进行http请求,获取本地的资源文件imageList.json中的内容,然后将这个json数据赋值给$scope.imageList,这样模板index.html中的imageList就有内容了。

这整个过程用到了angular两个核心概念:

  • 依赖注入:通过依赖注入,我们可以很方便的获取所需要的东西。这个$scope$http就是通过依赖注入的机制获得的,我们并不需要创建它们,只需要在demoController获取就行了。
  • 数据绑定:当我们获取数据赋值给当前作用域下的某个值后,例如$scope.imageList,模板index.html中的imageList也发生了变化。

imageList有值后,ng-repeat遍历这个imageList,然后多次实例化img元素,用法就和PHP中的for-in类似,只是少了for。

在实例化img时,使用了ng-srcng-style指令。
其中,ng-srcsrc一样,指定图片的地址。
ng-style则对应的是style,指定的是样式,但是它与style不同的是,它传递的是对象,而且可以通过函数获取一个对象。例如上面实现响应式布局,就是通过imageStyle返回了对应的高和宽。

不过,这种通过js设置样式的代码是不推荐的,因为在angular使用的是MVC的开发模式,M就是绑定的数据;V就是DOM,也就是我们的index.html模板;C则是控制器。上面,我们相当于在控制器做模板的事情,我们应该保持js纯洁性,详情可参考:细说Angular ng-class

3、添加分类、搜索和删除功能

当我们在显示列表的时候,也许想拥有菜单分类、搜索和删除的功能,这个时候在前面的代码基础上,如何实现呢?

这里是实现之后的index.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
<body ng-controller="demoController">
<div class="row">
<div class="col-sm-4 col-xs-12">
<div class="btn-group pull-left">
<button type="button" class="btn btn-default" ng-class="{'btn-primary': !search.type}" ng-click="selectType()">全部</button>
<button type="button" class="btn btn-default" ng-class="{'btn-primary': search.type == 1}" ng-click="selectType(1)">人物</button>
<button type="button" class="btn btn-default" ng-class="{'btn-primary': search.type == 2}" ng-click="selectType(2)">动物</button>
<button type="button" class="btn btn-default" ng-class="{'btn-primary': search.type == 3}" ng-click="selectType(3)">风景</button>
</div>
</div>
<div class="col-sm-4 col-xs-12" style="margin: 10px 0; height: 40px">
<input class="form-control" ng-model="search.keyword" placeholder="搜索内容"/>
</div>
<div class="col-sm-4 col-xs-12" style="line-height: 50px">
搜索:{{search.keyword}}
</div>
</div>
<div class="photos">
<div class="photoItem" ng-repeat="info in imageList | filter:search" ng-if="!info.is_hide">
<img ng-src="{{info.imageUrl}}" ng-style="imageStyle(info.width)"/>
<div class="block">
<button class="btn btn-default gap" ng-click="removePhoto(info)"><span>删除</span></button>
</div>
</div>
</div>
</body>

在demoApp.js文件中的demoController添加了下面一些内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$scope.search = {};
/**
* 删除图片
* @param index
*/
$scope.removePhoto = function (info) {
info.is_hide = true;
};
/**
* 按钮点击选择类型的事件
* @param type
*/
$scope.selectType = function (type) {
$scope.search.type = type;
};

这个实现之后就是我们的最终效果,当点击类型时,可以获取对应类型的图片,当输入关键字,可以搜索到相关的图片,还可以进行删除,这是怎么做到的呢?

首先,我们在左上角添加一排按钮,添加类型选择的功能,如下:

1
2
3
4
5
6
7
8
<div class="col-sm-4 col-xs-12">
<div class="btn-group pull-left">
<button type="button" class="btn btn-default" ng-class="{'btn-primary': !search.type}" ng-click="selectType()">全部</button>
<button type="button" class="btn btn-default" ng-class="{'btn-primary': search.type == 1}" ng-click="selectType(1)">人物</button>
<button type="button" class="btn btn-default" ng-class="{'btn-primary': search.type == 2}" ng-click="selectType(2)">动物</button>
<button type="button" class="btn btn-default" ng-class="{'btn-primary': search.type == 3}" ng-click="selectType(3)">风景</button>
</div>
</div>

对应的demoApp.js文件的内容有:

1
2
3
4
5
6
7
/**
* 按钮点击选择类型的事件
* @param type
*/
$scope.selectType = function (type) {
$scope.search.type = type;
};

我们在按钮上通过ng-click指令添加了事件监听,当点击按钮的时候,触发了selectType事件,随后就将search.type的值改变成了事件传递过来的值。

search.type值改变之后,由于按钮中我们添加了ng-class指令,从而enable了当前点击按钮的btn-primary类,这样就实现了选中效果。

ng-class是用来实现样式的,它有两种用法(通过函数传递就不说了):

  • 传递类名, 例如 <a ng-class="active selected" />,这样a标签就相当于给了两个样式类的效果,和<a class="active selected">一样
  • 通过true和false来控制,例如 <button ng-class="{'btn-primary': search.type == 1}">,当search.type为1时,button就拥有了btn-primary这个类的样式;否则就不拥有。

相比与ng-styleng-class它更能实现js与html代码的分离,保持js的纯洁,详情可参考:细说Angular ng-class

当我们点击类型按钮的时候,如何显示出对应类型的图片?这用到了ng-repeat的过滤器,具体代码在这里:

1
2
<div class="photoItem" ng-repeat="info in imageList | filter:search">
</div>

当使用ng-repeat遍历时,如果info中的数据与search中的数据不匹配,则ng-repeat会忽略,不会实例化元素。

这个匹配规则默认是这样的,当search是一个数字或字符串时,它会匹配info对象所有的值,例如,search=6,那么info={id: 6, type:2}和info={id:7, type:6}都会匹配到;而当search是个对象时,它就会将对应的键值进行匹配,例如search={type:2},它只会匹配info={id: 6, type:2}的对象,而不会匹配info={id: 2, type:7}。

当然,上面是ng-repeat的过滤器的默认匹配规则,我们可以后面添加第三个参数,详情可以参考filter

同样原理,我们实现搜索是通过ng-modelfilter实现的,这里就说说ng-model,代码如下:

1
2
3
4
5
6
<div class="col-sm-4 col-xs-12" style="margin: 10px 0; height: 40px">
<input class="form-control" ng-model="search.keyword" placeholder="搜索内容"/>
</div>
<div class="col-sm-4 col-xs-12" style="line-height: 50px">
搜索:{{search.keyword}}
</div>

这段代码通过ng-model,我们实现了双向绑定,当我们输入值之后,我们可以看到界面中搜索:后面的内容随着我们输入变化而变化,我们不需要在js中做任何事情就可以做到这些,这是一个特别棒的特性。

最后,我们来看看是如何实现删除的?
对应的index.html中的代码:

1
2
3
4
5
6
<div class="photoItem" ng-repeat="info in imageList | filter:search" ng-if="!info.is_hide">
<img ng-src="{{info.imageUrl}}" ng-style="imageStyle(info.width)"/>
<div class="block">
<button class="btn btn-default gap" ng-click="removePhoto(info)"><span>删除</span></button>
</div>
</div>

对应的demoApp.js中的代码:

1
2
3
4
5
6
7
/**
* 删除图片
* @param index
*/
$scope.removePhoto = function (info) {
info.is_hide = true;
};

操作主要是在按钮上添加一个removePhoto事件,当点击按钮的时候,将数据中的is_hide设置true,而在模板有ng-if="!info.is_hide",当is_hide为true时,表达式为false, 从而通过ng-if指令实现了删除功能。

除了,ng-if可以隐藏元素之外,还有ng-showng-hide,它们用法差不多,都是传递一个bool值进行控制。但是,它们之间是有些区别的:

  • ng-if 它实现隐藏的功能,是将元素从dom中删除,使用ng-if会创建子作用域。
  • ng-show 它实现隐藏的功能,是将元素中的display设置为none,它不会创建子作用域。
  • ng-hide 它实现隐藏的功能和ng-show一样,是设置元素样式中的display为none来实现的,它不会创建子作用域。
    到此,我们就实现了简单的图片列表应用。

三、指令补充

1、ng-app

这个指令指定了angular中demoApp模块作为它的根模块。使用ng-app指令时,需要注意的是,它一般都放在页面的根元素当中,例如或标签。还需要注意三点是,一个页面只能有一个angular应用;angular应用不能嵌套使用;不能和一些开启了transclusion的指令一起使用,例如ng-if、ng-include、ng-view(transclusion是什么参考彻底弄懂AngularJS中的transclusion)。

2、ng-repeat

ng-repeat会遍历所指定的对象,然后多次实例化所在的元素,用法就和PHP中的for-in一样,只是少了for,如下:

1
2
3
<div class="photoItem" ng-repeat="info in imageList">
<img ng-src="{{info.imageUrl}}" ng-style="imageStyle(info.width)"/>
</div>

而且,它还可以进行键值对访问,如下:

1
2
3
4
5
6
<div class="photos" >
<div class="photoItem" ng-repeat="(key, info) in imageList">
<img ng-src="{{info.imageUrl}}" ng-style="imageStyle(info.width)"/>
<span>{{key}}</span>
</div>
</div>

在使用ng-repeat时,需要注意的是,它会多次实例化模板,每个模板都会创建一个属于它自己的scope

在使用ng-repeat时,我们需要获取当前repeate的元素是第几个,这个时候我们就会用到里面的一个特殊变量$index

1
2
3
4
5
6
<div class="photos" >
<div class="photoItem" ng-repeat="info in imageList">
<img ng-src="{{info.imageUrl}}" ng-style="imageStyle(info.width)"/>
<span>{{$Index}}</span>
</div>
</div>

当使用ng-repeat时,有时候会涉及到嵌套,这个时候,我们就需要借助ng-init指令一起使用了,如下我曾经使用过的一种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="row" style="height:400px">
<div class="col-sm-4 col-xs-12" ng-repeat="data in dataList" ng-init="pIndex=$index">
<div class="well">
<h5 class="text-center text-theme">{{data.title}}</h5>
<div style="overflow: auto; border-bottom: 1px dashed red; margin-bottom: 10px;">
<div ng-repeat="item in data.itemList" style="border-top: 1px dashed #aaa; padding-bottom: 10px">
<input type="checkbox"
ng-click="selectItem(item, pIndex)"
ng-checked="item.item_status == 2"
ng-model="checked[item.project_module_item_id]"
>
<div ng-repeat="msg in item.item_detail | splitArr">
<p>{{$index+1}}. {{msg}}</p>
</div>
<p class="text-right">
<span class="text-theme">{{item.time}}</span>
</p>
</div>
</div>
</div>
</div>
</div>

还有一些更详细的知识,请参考官方文档ngRepeat

3、$watch监听数据变化

这个功能,我们在实时搜索时会用到,用法如下:

1
2
$scope.$watch('name', function () {
});

当监听的是一个对象的时,你想监听到这个对象每个属性的变化时,上面就会有问题,这个时候,需要传递第三个参数:

1
2
$scope.$watch('person', function () {
}, true);

三、总结

1、我们搭建开发环境时,尽量使用npm或bower进行集成式搭建,这样易于安装和管理。

2、简单罗列一些我们讲到的指令,也是经常用到的指令:

  • ng-app(指定应用的模块名,与angular.module一起使用)
  • ng-controller(指定了一个控制器,一个作用域范围,MVC)
  • $http(http请求,$http.get、$http.post来请求数据)
  • $scope (作用域)
  • ng-model (双向绑定)
  • ng-repeat (ng-init来操作嵌套循环)
  • ng-if & ng-show & ng-hide (ng-if会删除或创建dom,ng-show和ng-hide将元素的display设置为none来实现)
  • ng-style & ng-class(ng中设置样式用的,ng-style传一个对象,ng-class传多个样式类,还可以通过true和false来开启关闭)
  • ng-click(点击事件)
  • $watch(监听属性变量的变化)

参考

angular tutorial

angular 中文入门教程

快速搭建静态服务器的几种方法