Angular使用了应该有一个月了,一直想总结来着,但是比较懒,所以一直拖着。这次正好趁着分享,写一篇相关内容的文章,来总结下Angular的基本使用和常用指令。本来打算写的是入门教程,发现入门教程怎么写都感觉不如官网,所以放弃了,可以把这一篇当做入门教程的补充版。如果大家要看入门教程可以看官方的angular tutorial,中文版的话1.5版本的没找到,大家可以看angular 1.4
的angular 教程。
这里以一个简单的图片列表应用为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
,里面是我们填入的信息。
之后,我们安装angularJS
、jquery
和bootstrap
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
下有angular
和jquery
,这就说明我们安装成功了。
查看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.8
、jquery 3.1.1
和bootstrap 3.3.7
。
在安装完依赖之后,我们需要将我们的工程放入一个web环境下,例如apache等等。如果你没有对应的web环境,我们可以使用npm快速搭建一个,这里可参考快速搭建静态服务器的几种方法。
到此,我们的环境算是搭建好了,现在开启我们的旅程吧!
二、图片列表应用
1、导入文件
克隆或下载angularExample,将工程下面的demoApp.css
样式文件、image
文件夹和resource
文件夹copy到你的工程目录下,这是我们所需要样式、图片和资源。
创建index.html
和demoApp.js
,在index.html
文件中,导入angular
、jquery
和bootstrap
这三个框架,然后导入demoApp.css
和demoApp.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
指令,这个指令指定了angular
中demoApp
模块作为它的根模块,也指定了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-src
和ng-style
指令。
其中,ng-src
和src
一样,指定图片的地址。
而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-style
,ng-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-model
和filter
实现的,这里就说说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-show
和ng-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 中文入门教程
快速搭建静态服务器的几种方法