通过AngularJS仿豆瓣一刻的案例:https://github.com/zhongxiaolian/douban
AngularJS介绍
AngularJS是一款由Google公司开发维护的前端MVC框架,其克服了HTML在构建应用上的诸多不足,从而降低了开发成本提高了开发效率。
AngularJS和jQuery是有一定区别的,jQuery更准确的来说只是一个类库(类库指的是一系列函数的集合)以DOM作为驱动,而AngularJS则是一个框架(诸多类库的集合)以数据和逻辑作为驱动。
框架对开发的流程和模式做了约束,开发者需要遵照约束进行开发,更注重实际的业务逻辑。
MVC
在开始学习AngularJS之前需要先了解什么是MVC。它不是一种技术,而是一种开发模式,更准确的说是组织代码结构的方式,广泛应用于软件开发领域,更多应用在后端开发程序里,在最初的前端开发的过程中js,css,html混在一起,代码的结构十分混乱,而且代码之间的耦合度比较高,不利于后期代码的维护,后来被引入到前端开发中,由于受到前端技术的限制便有了一些调整,进而出现了很多MVC的衍生版(子集)如MVVM、MVW、MVP、MV*等。
MVC由模型(Model)、视图(View)、控制器(Controller)3部分构成,采用这种开发模式为合理组织代码、降低代码间耦合度提供了方便。
模型:一般用来处理数据,一般指操作数据库。
视图:一般用来展示数据,比如通过HTML展示。
控制器:一般用做连接模型和视图的桥梁。
接下来就该进入正题了。。。。。
模块化
使用AngularJS构建应用(APP)时是以模块化(module)的方式组织的,即将整个应用划分为若干个模块,每个模块都有各自的职责,最终组合成一个整体。
采用模块化的组织方式可以最大程度的实现代码的复用,可以像搭积木一样进行开发。
-
定义应用
通过为任一个HTML标签添加ng-app属性,可以指定一个应用,表示此标签所包裹的内容都属于应用(APP)。一般在HTML标签上添加。
-
定义模块
AngularJS提供了一个全局对象angular,在此全局对象下存在若干个方法,其中angular.module()方法用来定义一个模块。
//第一个参数表示模块的名称,第二个参数表示此模块依赖的其他模块。
var App = angular.module('App', []);
-
定义控制器
控制器作为连接模型和视图的桥梁而存在,所以当我们定义好了控制器也就定义好了模型和视图。
//App是一个模块实例,通过模块实例定义控制器。
//第一个参数表示控制器的名称,第二个参数是一个数组。
//除了数组的最后一个元素是函数外,其余元素都是字符串,表明该控制器所依赖的服务。
App.controller('DemoContoller', ['$scope', function ($scope) {
//$scope模型对象
$scope.name = 'liangyiluo';
$scope.school = '华北理工大学';
$scope.courses = [
'MVC',
'指令',
'模块化'
]
}]);
模型数据要展示到视图上,所以需要将控制器关联到视图上,通过为HTML标签添加ng-controller属性并赋值相应的控制器的名称,就确立了关联关系。
{{name}}在{{school}}学习使用AngularJS
- 第{{key+1}}天:{{course}}
以上就是AngularJS最基本的MVC工作模式。所以的AngularJS学习都会围绕下图展开。
指令
HTML在构建应用(APP)时存在诸多不足,AngularJS通过扩展一系列的HTML属性或标签来弥补这些缺陷,所谓指令就是就是AngularJS自定义的一系列的HTML属性或标签。这些指令都是以ng-作为前缀的。
- 内置指令
ng-app:指定应用根元素,至少有一个元素指定了此属性。
ng-controller:指定控制器。
ng-show:控制元素是否显示,true显示,false不显示。
ng-hide:控制元素是否隐藏,true隐藏,false不隐藏。
ng-if:控制标签是否存在,true存在,false不存在。
ng-src:图片路径。
ng-href:文件地址。
ng-class:控制类名。{"class1":true,"class2":true}
ng-include:引入模版。js不能读取文件,所以需要向服务器发送ajax请求模版。一般用于将多个页面的共同的头和尾抽离出来。
ng-disabled:表单禁用。
ng-readonlu:表单只读。
ng-checked:单/复选框选中。
ng-selected:下拉框选中。
注:更多指令请参照官方文档。 - 自定义指令
AngularJS允许根据实际业务需要自定义指令,通过angular全局对象下的directive方法实现。
App.directive('tag', function () {
return {
// E element
// A attribute
// C class
// M mark replace 必须为true
restrict: 'ECMA',
// template: '- 首页
- 列表
',
templateUrl: './list.html',
// replace: true
}
});
数据绑定
AngularJS是以数据作为驱动的MVC框架,所有模型里的数据经过控制器展示到视图中。
所谓数据绑定就是将模型中的数据与对应的视图进行关联,分为单向绑定和双向绑定两种方式。
- 单向绑定
- {{name}}{{age}}
- 双向绑定
双向绑定可以实现模型数据和视图模版的双向传递。
在AngularJS中通过{{}}或者ng-bind指令来实现模型数据向视图模版的绑定,模型数据通过一个内置服务$scope来提供,这个$scope是一个空对象,通过为这个对象添加属性或者方法便可以在相应的视图模版里被访问。
注:{{}}在获取数据时当页面刷新会有“闪烁”现象,给HTML标签添加ng-cloak可以解决(因为在页面加载时解析到{{}}时,页面无法解析,只有当angular.js文件被加载后才可以解析,所以把angular.js文件提前也可以解决这个问题)。通过ng-bind-template可以绑定多个数据。
通过为表单元素添加ng-model指令实现视图模版向模型数据的绑定。
{{msg}}
- 事件绑定
AngularJS对事件也进行了扩展,通过在原有事件名称的基础上添加ng-前缀,然后以属性的形式添加到对应的HTML标签上即可,如ng-click、ng-dbclick等。
通过ng-repeat可以将数据遍历到视图中
姓名
性别
年龄
{{star.name}}
{{star.sex}}
{{star.age}}
通过ng-switch on可以对数据进行筛选(类似switch)
-
{{item}}
- 不同控制器之间的数据传递
NG中多模块开发不是多个模块再去使用多个ng-app,而是先确定主模块,其他的子模块添加到主模块的中括号中,进行注入。那么在主模块中就可以使用子模块了。
可以参照具体项目:https://github.com/zhongxiaolian/douban
那么继续研究控制器之间的数据传递。
var app=angular.module('mainApp',[]);
app.controller('SelfCtrl', function($scope) {
$scope.click = function () {
//$broadcast广播to-child事件的名称,广播的内容是child
$scope.$broadcast('to-child', 'child');
//$emit事件发射器to-parent是事件的名称 parent是传的内容
$scope.$emit('to-parent', 'parent');
}
});
app.controller('ParentCtrl', function($scope) {
$scope.$on('to-parent', function(event,data) {
console.log('ParentCtrl', data);
});
});
app.controller('ChildCtrl', function($scope){
$scope.$on('to-child', function(event,data) {
console.log('ChildCtrl', data);
});
});
作用域
通常AngularJS中应用是由若干个视图组合而成的,而视图又都是HTML元素,并且HTML元素是可以相互嵌套的,另一方面视图都隶属于某个控制器,进而控制器之间也必然会存在嵌套关系。每个控制器又对应一个模型也就是$scope对象,不同层级控制器下的$scope便产生了作用域。
- 根作用域
一个AngularJS应用在启动时会自动创建一个根作用域$rootScope,这个根作用域在整个应用范围都是可以被访问到的。
//在根作用域初始化数据
- 子作用域
通过ng-controller指令可以创建一个子作用域,新建的作用域可以访问其父作用域中的数据。
过滤器
在AngularJS中使用过滤器格式化展示数据,在{{}}中使用 “|”来调用过滤器,使用“:”传递参数。
- 内置过滤器
1、currency;将数值格式转换为货币格式
2、date:格式化日期
3、filter:在给定数组中选择满足条件的一个子集,并返回一个新数组,其条件可以是一个字符串、对象、函数。
4、json:将javascript对象转换为JSON字符串。
5、limitTo:取出字符串或数组的前(正数)几位或者后(负数)几位。
6、lowercase:转小写
7、uppercase:转大写
8、number:数字格式化,可控制小数位数。
9、orderby:对数组进行排序,第二个参数可控制方向。
- {{price|currency:'¥'}}
- {{now|date:'yyyy/MM/dd hh:mm:ss'}}
- {{items|filter:'s'}}
- {{students|filter:{age: 16} }}
- {{students|json}}
- {{items|limitTo:-2}}
- {{str|uppercase|limitTo:3}}
- {{str|lowercase}}
- {{num|number:2}}
- {{items|orderBy: '':false}}
- {{students|orderBy: 'age': true}}
- 自定义过滤器
除了使用AngularJS内置的过滤器外,还可以根据业务需要自定义过滤器,通过模块实例对象的filter方法自定义过滤器。
var App = angular.module('App', []);
//这个过滤器要实现的功能是首字母大写
App.filter('capitalize', function () {
//过滤器实际上就是一个函数,第一个参数表示要处理的数据,第二个参数表示给过滤器传递的参数。
return function (input, arg2) {
return input[0].toUpperCase() + input.slice(1);
}
});
依赖注入
AngularJS采用模块化的方式组织代码,将一些逻辑封装成一个对象或函数,实现最大程度复用,这导致了使用者和被使用者之间存在依赖关系。
所谓依赖注入是指在运行时自动查找依赖关系,找到后传递给使用者的一种机制。
常见的AngularJS的内置服务有$http、$location、$timeout、$rootScope等。
- 推断式注入
没有明确声明依赖,AngularJS会将函数参数名称当成是依赖的名称。
var App = angular.module('App', []);
App.controller('DemoController', function ($scope, $http) {
});
注:这种方式存在一个问题,当代码压缩后函数的参数被压缩,这样便会造成依赖无法找到。
- 行内注入
以数组形式明确声明依赖关系,数组元素都是包含依赖名称的字符串,数组最后一个元素是依赖注入的目标函数。
var App = angular.module('App', []);
// 行内式注入,推荐使用这种依赖注入方式。
App.controller('DemoController', ['$scope', '$http', function ($scope, $http) {
$scope.name = '依赖注入';
}]);
服务
服务是一个对象或函数,对外提供特定的功能。
- 内置服务
1、$location是对原生Javascript的location对象的属性和方法的封装。
2、$timeout和$interval对原生Javascript的setTimeout和setInterval进行了封装。
var App = angular.module('App', []);
App.controller('DemoController', ['$scope', '$timeout', '$interval',function ($scope, $timeout, $interval) {
$timeout(function () {
$scope.msg = '执行了';
}, 3000);
var timer = $interval(function () {
$scope.now = new Date;
}, 1000);
$scope.stop = function () {
$interval.cancel(timer);
}
}]);
- $filter在控制器中格式化数据
var App = angular.module('App', []);
App.controller('DemoController', ['$scope', '$filter', function ($scope, $filter) {
$scope.price = 11.11;
var currency = $filter('currency');
$scope.price = currency($scope.price,"¥");
$scope.str = 'hello angular';
var uppercase = $filter('uppercase');
$scope.str = uppercase($scope.str);
$scope.str1 = $filter('limitTo')($scope.str, 2);
}]);
- $log打印调试信息
var App = angular.module('App', []);
App.controller('DemoController', ['$log', function ($log) {
$log.info('普通信息');
$log.warn('警告信息');
$log.error('错误信息');
$log.log('打印信息');
$log.debug('调试信息');
}]);
- $http用于向服务器发ajax请求
var App = angular.module('App', []);
App.controller('DemoController', ['$scope', '$http', '$log', function ($scope, $http, $log) {
$http({
url: 'example.php',
method: 'post',
headers: {
// 'Content-Type': 'application/x-www-form-urlencoded'
//上面这种请求头会把请求数据设置成form-data形式
'Content-Type': 'application/json;charset=UTF-8'
//默认的请求头信息(Restful 发送的是JSON数据)
},
// get 参数
params: {
name: 'itcast',
sex: '男'
}
// post参数
data: {
age: 10
}
}).success(function (info) {
$log.info(info);
});
}]);
// 接口方式
// SOAP(key=val&key=val,这种形式叫formData,Content-Type 设成 application/x-www-form-urlencoded,PHP后台可以用$_POST来接收)
// RESTFUL(json数据,这种形式叫Request Payload,Content-Type 设成 application/json;charset=UTF-8,也是默认的,PHP后台可以用file_get_contents("php://input") )
通过JSONP获取天气信息
var App = angular.module('App', []);
App.controller('WeatherController', ['$scope', '$http', function($scope, $http) {
$http({
method: 'jsonp', // 支持jsonp
//callback=JSON_CALLBACK是固定写法,服务器默认获取callback属性值,JSON_CALLBACK是一个占位符,AngularJS底层会自动生成一个函数替换它(被服务器调用,接收服务器返回的数据)。
url: 'http://api.map.baidu.com/telematics/v3/weather?callback=JSON_CALLBACK',
params: {
location: '北京',
output: 'json',
ak: '0A5bc3c4fb543c8f9bc54b77bc155724'
}
})
.success(function (data) {
// 请求回的数据放到模型上
$scope.weatherData = data.results[0].weather_data;
});
}])
- 自定义服务
手动将一些通用性的逻辑功能进行封装,方便以后使用。服务本质上就是一个对象或函数,所以自定义服务就是要返回一个对象或函数以供使用。
1、factory方法
var App = angular.module('App', []);
// 通过factory定义一个名叫showTime的服务
App.factory('showTime', ['$filter', function ($filter) {
var now = new Date();
var date = $filter('date');
return {
now: date(now, 'y-M-d H:m:s')
}
}]);
App.controller('DemoController', ['$scope', 'showTime', function($scope, showTime) {
$scope.now = showTime.now;
}])
2、service方法
var App = angular.module('App', []);
// 通过service方法自定义服务显示日期
App.service('showTime', ['$filter', function($filter) {
var now = new Date();
var date = $filter('date');
//不需要return,直接赋值给this
this.now = date(now, 'y-M-d H:mm:ss');
}]);
App.controller('DemoController', ['$scope', 'showTime', function($scope, showTime) {
$scope.now = showTime.now;
}])
3、value方法
var App = angular.module('App', []);
// 自定义常量服务
App.value('author', 'itcast');
App.value('version', '1.0');
// 本质上是一个服务
// 从表现形式上是一个常量
// 常量就是不变的值与变量相对应
// 声明依赖调用服务
App.controller('DemoController', ['$scope', 'author', 'version', function($scope, author, version) {
$scope.author = author;
$scope.ver = version;
}]);
模块加载
AngularJS模块可以在被加载和执行之前对其自身进行配置。
- 配置模块
通过config方法实现对模块的配置,AngularJS中的服务大部分都对应一个provider,用来执行与对应服务相同的功能或对其进行配置。
比如$log,$http,$location都是内置服务,相对应的provider分别是$logProvider,$httpProvider,$locationProvider。
下面以$log和$filter模块为例进行演示:
{{now}}
{{str|capitalize}}
- 运行模块
服务是以模块的形式存在的且对外提供特定的功能,之前都是将服务作为依赖,注入到相应的控制器,然后在控制器中进行调用,除了这种方式我们还可以直接运行服务模块,AngularJS提供了run方法。
{{name}}
不但如此,run方法还是最先被执行的,利用这个功能我们可以将一些需要优先执行的功能通过run方法来运行,比如验证用户是否登录,未登录则不允许进行任何操作。
路由
- 单页面应用的介绍
一个应用是由多个视图组成的,需要根据不同的业务逻辑展示给用户不同的视图,路由则是实现这一功能的关键,但页面应用可以提升性能,增强用户体验。
SPA(Single Page Application):指的是通过单页面展示所有功能,通过Ajax动态获取数据然后实时进行渲染,结合CSS3动画模仿原生APP交互,然后在进行打包(使用工具把Web应用包一个壳,这个壳本质上就是一个浏览器)变成一个“原生应用”。
在PC端也有广泛的应用,通常情况下使用Ajax异步请求数据,然后实现内容的局部刷新,局部刷新的本质就是动态生成DOM,新生成的DOM元素并没有真实存在于文档中,所以当再次刷新时新添加的DOM元素会“丢失”,通过单页面应用可以很好的解决这个问题。
路由。 - 路由的使用
在后端开发过程中通过URL地址可以实现页面(视图)的切换,但是AngularJS是一个纯前端的MVC框架,在开发但页面应用时,所有功能都在同一页面完成,所以无需切换URL地址(即不允许产生跳转),但WEB应用中又经常通过链接(a标签)来更新页面(视图),当点击链接时还要阻止其向服务器发起请求,通过锚点(页内跳转)可以实现这一点。
实现单页面应用需要具备:
1、只有一个页面。
2、链接使用锚点。
3、服务器。
下面是一个简单的使用NG路由的例子:
单页面应用原理:
Index Page
通过上面的例子发现可以通过监听hashchange事件监听到锚点的变化,进而可以实现为不同的锚点绑定不同的视图,单页面应用就是基于这个原理实现的。
注:在1.2版本之前路由器的功能是包含在AngularJS核心代码中的,之后的版本将路由器独立成一个模块,下载angular-route.js。
- 路由参数
1、$routeProvider提供两个方法匹配路由,分别是when和otherwise,when需要两个参数,othersize方法作为when方法的补充只需要一个参数,其中when方法可以被多次调用。
2、第一个参数是一个字符串,代表当前URL的hash值。
3、第二个参数是一个对象,用于配置当前路由的视图、控制器等。
template:字符串形式的模版。
templateURL:引入外部视图模版。
controller:视图模版所属的控制器。
redirectTo:跳转到其他路由。
4、获取路由传递的参数,在控制器中注入$routeParams可以获取路由传递的参数。
内置jQuery
在没有引入jQuery之前,NG实现了简版的jQuery Lite,通过angular.element不能选择元素,但是可以将一个DOM元素转换成jQuery对象,如果提前引入了jQuery则angular.element则完全等于jQuery。