在如今的互联网,传统的页面刷新的用户交互已经过时了,而且使用后台渲染技术如JSP等也不符合当今互联网发展趋势,因为后台渲染会大大消耗服务器的资源,现在大多数网站的搭建都是使用html + js的方式来搭建,把数据的交互都交给客户执行,客户端通过Ajax异步交互从数据库中获取数据,而不用每次请求都刷新一次页面,说起交互,那就不得不提现在流行的单一页面应用程序了,单一页面指的是一个web应用只有一个页面,通过Ajax异步避免页面刷新提高网站的响应性,具有应用程序的良好体验,AngularJS就是为此而生的。
表达式
AngularJS的表达式在页面中是以双大括号{{expression}}来表示的,大括号里面可以填写文字,表达式,变量等。
注:由于双大括号会被网页解释导致看不到,所以本文代码块中的AngularJS表达式都用中文双大括号{{}}表示。
- 变量:
<div>合计: {{sum}}元</div>
sum变量是从$scope作用域拿的,后面会讲到。
- 表达式:
<p>我的第一个表达式: {{5+5}}</p>
- 字符串:
<div ng-app="" ng-init="firstName='John';lastName='Doe'">
<p>姓名: {{firstName + " " + lastName}}</p>
</div>
指令
AngularJS的指令可扩展html属性,AngularJS可通过指令与页面进行交互,且可以允许开发者自定义指令。AngularJS的指令都有ng-前缀,如下:
- ng-app:在页面上初始化一个AngularJS应用程序
<div ng-app="">
<p>你输入的为: {{1+1}}</p>
</div>
如果ng-app=”“,那么会使用AngularJS默认初始化一个应用程序,如果不为空,则需要自定义一个应用程序:
angular.module('myApp', ['ngRoute'])
.config(['$locationProvider', '$routeProvider', function ($locationProvider, $routeProvider) {
......
}]).controller('AppCtrl', ['$scope','$http', function ($scope,$http) {
......
}])
<div ng-app="myApp">
<p>你输入的为: {{1+1}}</p>
</div>
- ng-model(模型):可把作用域的值绑定到应用程序中,或者可以把元素值绑定到输入域中,这种绑定采用双向绑定机制,即任何一方改变,另一方也跟着改变。
元素绑定:
<div ng-app="" ng-init="firstName='John'">
<p>在输入框中尝试输入:</p>
<p>姓名:<input type="text" ng-model="firstName"></p>
<p>你输入的为: {{firstName}}</p>
</div>
作用域绑定:
angular.module('myApp')
.controller('AppCtrl', ['$scope','$http', function ($scope,$http) {
$scope.shop.name="zhangsan"
}])
<div ng-app="myApp">
<div class="form-group" ng-controller="AppCtrl">
<input type="text" class="form-control" ng-model="shop.name">
</div>
</div>
- 其它指令
AngualrJS的指令非常丰富,比如ng-controller绑定一个控制器、ng-repeat类似于for循环指令、ng-if类似于if判断指令、ng-click点击事件等等,都是非常好用的指令。
作用域
在说作用域之前,我们先了解一下AngualrJS应用的组成:
- veiw:视图,即html页面;
- model:模型,即页面所需要的数据;
- Controller:控制器,可以对模型的数据进行修改或添加或删除。
前面我们有提到过作用域这东西,指的就是model,它是AngualrJS在HTML域JS之间传值的一个非常重要的东西,通常我们从后台请求回来的数据都会放在作用域中,然后HTML通过作用域拿到数据,展示给用户。$scope只作用于其所在的控制器ng-controller包含的html。还有一个$rootScope根作用域,它是作用于ng-app包含的html。
控制器
模型的数值由控制器控制,因此控制器通常操作数据,把数据存到$scope作用域中。控制器有ng-controller指令定义。
angular.module('myApp')
.controller('ShopCtrl', ['$scope', '$http', function ($scope, $http) {
function requestTableData() {
$http({
method: "GET",
url: HostManageService._shopHost + "/api/shop/shops/" + $scope.searchParams.page,
}).then(function (data, status) {
$scope.tableData = data.shop;
})
}
requestTableData();
}])
......
<div ng-app="myApp">
<table ng-controller="ShopCtrl">
<thead>
<tr>
<th>姓名</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="t in tableData">
<td>{{t.name}}</td>
</tr>
</tbody>
</table>
</div>
......
过滤器
过滤器在表达式中用竖杠右边加过滤器名字表示:
<div ng-app="myApp" ng-controller="personCtrl">
<p>姓名为{{lastName | uppercase}}</p>
</div>
这里使用了AngularJS的默认过滤器uppercase,表示把字符串转换成大写。
也可以在指令中加过滤器:
<div ng-app="myApp" ng-controller="namesCtrl">
<ul>
<li ng-repeat="x in names | orderBy:'country'">
{{x.name + ',' + x.country}}
</li>
</ul>
</div>
这里意思是列表按照country来排序。
我们也可以自定过滤器:
//金额积分千分位格式
.filter('AmountIntegralThousandsFormat', function () {
return function (input) {
if(typeof(input)=="undefined"){
return;
}
return input.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
});
<tr>
<td>积分成本费</td>
<td>{{platformData.provisionsReceivables | AmountIntegralThousandsFormat}}元</td>
</tr>
服务
服务即是可以被注入到可注入的地方的一种依赖,Angular自定义服务有很多个方法,每个方法又有区别,但他们都是$provide的封装形式。而Angular本身也提供了很多非常优秀的服务,如$Http,$scope,$routeParams,$q等等,他们有个共同的特征,即前缀都是美元符号$。
在写自定服务之前,先来讲讲Angular的MVC设计模式,这种模式思想与后台的MVC设计思想如出一致,据说Angular是一位后台开发者为了方便操作前端页面而发明的,所以Angular也参照了后台MVC的分层思想:
Dao层:在Angular中指的是model,主要用来写Ajax从后台获取数据;
Service层:在Angular中指的是服务,主要用来写前端业务,也可充当数据容器;
Controller层:在Angular中指的是控制器,主要用来处理数据,它最终的目的是为了向页面提供需要展示的数据,且不提倡在控制器中写过多的逻辑代码。
value
Value是一个简单的值对象,用于向控制器传递值。
var myApp = angular.module("mainApp", []);
myApp.value("defaultInput", 5);
myApp.controller('CalcController', ['$scope','defaultInput', function($scope, defaultInput) {
$scope.number = defaultInput;
}]);
service
用service创建服务,是一个构造函数,Angular会用new来创建一个对象,因此用service创建服务无需返回对象,里面属性通常用this表示:
var app = angular.module('app' ,[]);
app.service('people', function () {
this.name = 'zhangsan';
});
app.controller('myCtrl', ['people', function (people) {
$scope.name = people.name;
}]);
注:要把people服务注入myCtrl控制器里,我们通常使用内联样式,即把服务的名字以数组元素的形式与函数组成一个数组,其中函数的传入参数即为数组元素,这样可以有效防止js文件以压缩形式传送过程中服务名字丢失的情况。
factory
factory也就是一个普通的Function函数,需要提供一个返回的值:
var app = angular.module('app' ,[]);
app.factory('people', function () {
var service = {};
service.name = "zhangsan";
var age;
service.setAge = function(newAge){
age = newAge;
}
service.getAge = function(){
return age;
}
return service;
});
app.controller('myCtrl', ['people', function (people) {
people.setAge = 20;
$scope.age = people.getAge;
$scope.name = people.name;
}]);
$provide
$provide叫提供商,而服务会通过提供商来定义,它是service和factory的老大哥,因为他们都是provide的高度封装形式,其实上面的写法是利用了Angular的语法糖,service和factory也可以通过如下来写:
var app = angular.module('myApp', []);
app.config(['$provide', function($provide) {
$provide.service('people', function() {
this.name = "zhangsan";
})
}]);
app.controller('myCtrl', ['$scope', 'people', function($scope, people) {
$scope.name = people.name;
}])
var app = angular.module('myApp', []);
app.config(['$provide', function($provide) {
$provide.service('people', function() {
var service = {};
service.name = "zhangsan";
var age;
service.setAge = function(newAge){
age = newAge;
}
service.getAge = function(){
return age;
}
return service;
})
}]);
app.controller('myCtrl', ['$scope', 'people', function($scope, people) {
people.setAge = 20;
$scope.age = people.getAge;
$scope.name = people.name;
}])
$provide是唯一一个可以写在config函数中的服务,config是用于在Angular启动前的配置工作,如我们需要预先定义路由(下面会讲到):
angular.module('myApp', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/shop/shops', {
templateUrl: '/shops/shops.html',
controller: 'ShopShopsCtrl'
})
}])
注:这里需要注意再config函数中注入provide服务,参数名的格式必须是:服务名 + Provider。
现在我们编写一个“原生”的服务:
var app = angular.module('myApp', []);
app.provider('people', function() {
this.$get = function() {
var service = {};
service.name = "zhangsan";
var age;
service.setAge = function(newAge){
age = newAge;
}
service.getAge = function(){
return age;
}
return service;
}
})
app.controller('myCtrl', ['$scope', 'people', function($scope, people) {
people.setAge = 20;
$scope.age = people.getAge;
$scope.name = people.name;
}])
注:provider不可注入内置服务。
有没有发现,它跟用factory创建people服务是很类似,但是factory创建看起来简洁许多了,这也是为什么说factory是provide的高度封装了,这里的$get函数用来返回provide的一个实例,且是必须的,否则会创建失败,为什么说这个get函数是必须的呢?
我们再来谈谈Angular的$injector注入器,它是负责创建provide创建的服务的实例,只要provide提供了一个带有参数的可注入的函数,我们就可以通过注入器获取这个服务的实例,比如我们现在要获取上面我们已经创建好的people服务:
var people = $injector.get("people");
people.setAge = 20;
var age = people.getAge;
var name = people.name;
$http
$http可以说是Angular最为核心的服务之一了,它是Angular高度封装Ajax的一个服务,用于异步请求后台数据,可以说它就是angular的Dao层了,它的基本写法如下:
// 简单的 GET 请求,可以改为 POST
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// 请求成功执行代码
}, function errorCallback(response) {
// 请求失败执行代码
});
摘取一段在项目中的实例:
function requestTableData() {
if ($scope.searchParams.isSearching) {
AlertService.alert("搜索进行中。。。");
return;
}
$scope.searchParams.isSearching = true;
$http({
method: "GET",
url: HostManageService._shopHost + "/api/shop/shops/"+$scope.searchParams.page,
data: {
shopCode: $scope.searchParams.shopCode,
shopName: $scope.searchParams.shopName,
industry: $scope.searchParams.industry,
shopStatus: $scope.searchParams.status
}
}).then(function successCallback (response) {
if (response.data.errcode == 0) {
if (response.data.shop.data.length == 0) {
AlertService.alert("暂时没有搜索数据,请重新搜索!");
$scope.tableData = [];
} else {
$scope.tableData = response.data.shop.data;
}
$scope.searchParams.page = response.data.shop.page;
$scope.searchParams.hasNext = response.data.shop.hasNext;
} else {
AlertService.alert(response.data.errmsg);
}
$scope.searchParams.isSearching = false;
}, function errorCallback(response) {
AlertService.alert("列表内容请求出错了!");
$scope.searchParams.isSearching = false;// 开启重新搜索
});
}
requestTableData();
也可以简写成以下形式:
$http.get('/someUrl', config).then(successCallback, errorCallback);
$http.post('/someUrl', data, config).then(successCallback, errorCallback);
路由
使用Angular路由可以实现单页面多视图的web应用,它不同于普通的url,如:http://localhost:9010/index/book,我们在Angular需要写成 http://localhost:9010/index#/book的形式,#号后面都是会被浏览器所忽略的这样无论我们请求http://localhost:9010/index#/book,还是http://localhost:9010/index#/computer,访问的都是http://localhost:9010/index而已,Angular路由正是利用了这点,它会识别#号后面的视图,并绑定相应的视图和控制器。
/book和/computer都有相应视图和控制器,配置如下:
angular.module('myApp', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/book', {
templateUrl: '/book.html',
controller: 'BookCtrl'
})
.when('/computer', {
templateUrl: '/computer.html',
controller: 'ComputerCtrl'
})
.otherwise({redirectTo:'/'});
}])
.controller('indexCtrl', ['$scope', function($scope) {
$scope.title = "my route";
}])
.controller('BookCtrl', ['$scope', function($scope) {
$scope.book.name = "Thinking in Java";
$scope.book.price = "$19.9";
}])
.controller('ComputerCtrl', ['$scope', function($scope) {
$scope.computer.name = "HP Pavilion DV4";
$scope.computer.price = "$699";
}])
创建Angular应用时需要包含ngRoute路由模块,且还需要载入angular-route.js文件,在congfig程序里注入$routeProvider服务来定义路由规则,路由配置对象语法规则如下:
$routeProvider.when(url, {
template: string,// 插入简单的 HTML 内容
templateUrl: string,// 插入 HTML 模板文件
controller: string,// controller名字
controllerAs: string,// controller别名
redirectTo: string, function,// 重定向地址
resolve: object<key, function>// 指定当前controller所依赖的其他模块。
});
接下来需要写页面:
- index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" ng-app="myApp">
<head>
<meta charset="utf-8" lang="zh">
<title>index</title>
</head>
<body ng-controller='indexCtrl'>
<h2>{{ title }}</h2>
<ul>
<li><a href="#/book">图书资料</a></li>
<li><a href="#/computer">计算机资料</a></li>
</ul>
<div ng-view></div>
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
<script src="http://apps.bdimg.com/libs/angular-route/1.3.13/angular-route.js"></script>
</body>
</html>
- book.html
<div>
bookName: {{book.name}}
bookPrice: {{book.price}}
</div>
- computer.html
<div>
computerName: {{computer.name}}
computerPrice: {{computer.price}}
</div>
这里有个很重要的指令:ng-view,他会把视图模板的内容都存放在它所在的元素内,也就是说它的内容会随着模板的不同而不同。除了ng-view指令所在的区域会变化之外,index.html其余区域都不会改变,这样就实现了单页面多视图的web应用了。