使用Node.js+Express+MongoDB+AngularJS构建应用程序(四)——使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

本系列文章将分四部分详细介绍使用Node.js、Express、MongoDB和AngularJS来构建应用程序,内容涵盖以下几部分:

第一部分:使用Node.js和Express构建RESTful Api

第二部分:MongoDB数据库基本使用

第三部分:使用Node.js+Express+MongoDB整合构建RESTful Api

第四部分:使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例


本文是《使用Node.js + Express + MongoDB + AngularJS构建应用程序》系列文章之四:《使用AngularJS调用Node.js + Express + MongoDB RESTful 接口实例》,本文将介绍使用AngularJS和ngResource来调用前面使用Node.js、Express和MongoDB完成的RESTful API实例。

程序运行效果:

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

 

一、程序构架

先来构建一个AngularJS程序,引入需要用到的库和文件。这里样式使用Pure样式框架。整个程序目录构架如下图。

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

目录各主要文件内容如下:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例</title>
	<link rel="stylesheet" type="text/css" href="lib/pure-min.css">
	<link rel="stylesheet" type="text/css" href="css/style.css">
	<script src="lib/angular.min.js"></script>
	<script src="lib/angular-resource.min.js"></script>
	<script src="js/app.js"></script>
</head>
<body>
	<div id="container" ng-controller="indexCtrl">
		<h1 class="center title">使用AngularJS调用Node.js+Express+MongoDB<br />RESTful接口实例</h1>
		<hr />

	</div>
</body>
</html>
#container{width: 100%; overflow: hidden;padding-left:5%; padding-right: 5%;box-sizing: border-box;}
.center{margin: 0 auto; text-align: center;}
.title{margin:10px 0;}

运行效果如下图所示。

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

 

二、前端页面构建

前端页面(index.html)主要分为两部分,一是显示现有用户的列表;二是显示添加用户和修改用户的表单,添加用户和修改用户使用同一表单。

页面布局引入了pureCSS框架,页面布局效果如下图:

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

以上是页面两部分内容全部显示的效果。前端页面构建部分相对简单,做好布局后,列表部分使用ng-repeat加入循环,表单部分做好数据绑定就可以了。为了让默认情况下只显示用户列表,点击“添加用户”或“修改用户”时才显示表单,我们再给表单部分添加ng-show指令,由控制器控制显示状态。此部分相对简单易懂,在这里不详细讲,看代码即可明白。

index.html页面代码如下:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例</title>
	<link rel="stylesheet" type="text/css" href="lib/pure-min.css">
	<link rel="stylesheet" type="text/css" href="css/style.css">
	<script src="lib/angular.min.js"></script>
	<script src="lib/angular-resource.min.js"></script>
	<script src="js/app.js"></script>
</head>
<body>
	<div id="container" ng-controller="indexCtrl">
		<h1 class="center title">使用AngularJS调用Node.js+Express+MongoDB<br />RESTful接口实例</h1>
		<hr />
		<button class="pure-button pure-button-primary addUser" ng-click="showAddUser()">添加用户</button>
		<table class="pure-table center tableList" width="100%">
		    <thead>
		        <tr class="center">
		            <th>编号</th>
		            <th>姓名</th>
		            <th>年龄</th>
		            <th>性别</th>
		            <th>工作</th>
		            <th>操作</th>
		        </tr>
		    </thead>

		    <tbody>
		        <tr ng-class-odd="'pure-table-odd'" ng-repeat="user in users">
		            <td>{{user.id}}</td>
		            <td>{{user.name}}</td>
		            <td>{{user.age}}</td>
		            <td>{{user.gender}}</td>
		            <td>{{user.job}}</td>
		            <td>
		            	<button class="pure-button button-secondary" ng-click="updateUser(user._id)">修改</button>
		            	<button class="pure-button button-error" ng-click="deleteUser(user._id)">删除</button>
		            </td>
		        </tr>
		    </tbody>
		</table>

		<hr />

		<form class="pure-form pure-form-aligned userForm" ng-show="showUserForm">
		    <fieldset>
		        <div class="pure-control-group">
		            <label for="userid">ID</label>
		            <input id="user_id" type="text" placeholder="id" ng-model="user._id" ng-disabled="true">
		        </div>

		        <div class="pure-control-group">
		            <label for="userid">编号</label>
		            <input id="userid" type="number" placeholder="编号" ng-model="user.id" ng-disabled="isLock">
		        </div>

		        <div class="pure-control-group">
		            <label for="name">用户名</label>
		            <input id="name" type="text" placeholder="用户名" ng-model="user.name">
		        </div>

		        <div class="pure-control-group">
		            <label for="age">年龄</label>
		            <input id="age" type="number" placeholder="年龄" ng-model="user.age">
		        </div>

		        <div class="pure-control-group">
		            <label for="gender">性别</label>
		            <!-- <input id="gender" type="text" placeholder="性别" ng-model="user.gender"> -->
		            <select id="state" class="pure-input-1-2" style="width:29%;" ng-model="user.gender">
	                    <option value="男">男</option>
	                    <option value="女">女</option>
	                    <option value="未知">未知</option>
	                </select>
		        </div>

		        <div class="pure-control-group">
		            <label for="job">工作</label>
		            <input id="job" type="text" placeholder="工作" ng-model="user.job">
		        </div>

		        <div class="pure-controls">
		            <button class="pure-button pure-button-primary" ng-click="addUser(user)" ng-show="showAddBtn">添加</button>
		            <button class="pure-button pure-button-primary" ng-click="handleUpdateUser(user)" ng-show="showUpdateBtn">修改</button>
		        </div>
		    </fieldset>
		</form>
	</div>
</body>
</html>
#container{width: 100%; overflow: hidden;padding-left:5%; padding-right: 5%;box-sizing: border-box;}
.center{margin: 0 auto; text-align: center;}
.title{margin:10px 0;}
.tableList{margin-top: 10px;margin-bottom: 10px;}
.pure-button{padding:0.3em 1em; font-size: 0.9em;}
.button-error {background: rgb(202, 60, 60);color:#fff;}
.button-secondary {background: rgb(66, 184, 221); color:#fff;}
.userForm{border: 1px solid #ccc; box-sizing: border-box;padding:10px;}

 

二、程序逻辑

因本程序是一个示例,功能非常简单,只是实现用户数据的增删改查,因此,逻辑部分的服务、控制器、配置、常量定义等都放在app.js一个文件中。程序逻辑部分主要分为几个部分,现作详细介绍。

首先,再贴一下初始状态的app.js

angular.module('myApp', ['ngResource'])

.controller('indexCtrl', ['$scope', function($scope){

}]);

目前的程序逻辑部分非常简单,一个模块,注入了ngResource依赖,还有一个控制器。

另外,在这里特别提出的是,程序中同时使用了id和_id,这两个是不一样的,id是数据集自身定义的一个属性字段,与name,age,gender等性质相同。而_id是我们在MongoDB中插入记录时,MongoDB自动帮我们生成的索引主键,两者性质和作用都不一样。

1、API路由路径常量定义

.constant('API_URL', 'http://localhost:3000/user')

2、使用ngResource构造用户操作服务

用户CRUD操作服务:

.factory('User', function($resource, API_URL){
	return $resource(API_URL, {},{
		'get': {method: 'GET'},
		'post': {method:'POST'},
		'update': {method: 'PUT'}
	});	
})

获取单个用户信息及删除用户数据服务:

.factory('GetUser', function($resource, API_URL){
	return $resource(API_URL + '/:id', {id: '@id'},{
		'get': {method: 'GET'},
		'delete': {method: 'DELETE'}
	});	
})

注:此处将删除用户数据与获取单个用户数据封在一个服务中实属无奈,原因是无法将删除用户数据的接口放在’User’服务中,因为通过DELETE方法,我们无法在后端通过req.body获取到从客户端传过来的ID值。

请看以下文章:

http://stackoverflow.com/questions/22186671/angular-resource-delete-wont-send-body-to-express-js-server

 

3、控制器部分

(1)注入必要的控制器依赖

主要有$scope,$window(作跳转),注入常量API_URL,注入服务User、GetUser等

(2)初始化

$scope.showUserForm = false;	//是否显示表单
$scope.showAddBtn = true;		//是否显示添加按钮
$scope.showUpdateBtn = false;	//是否显示修改更新按钮
$scope.users = [];				//用户列表数组
$scope.user = {};				//单个用户数据
$scope.isLock = false;			//是否锁定,一个状态,修改用户信息时不能修改ID

(3)获取所有用户数据

User.get({}).$promise.then(function(res){
	if(res.status){
		$scope.users = res.data;
	}
});

运行效果:

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

(4)添加用户按钮响应和添加用户操作

//添加用户按钮响应
$scope.showAddUser = function(){
	$scope.showAddBtn = true;
	$scope.showUpdateBtn = false;
	$scope.showUserForm = true;
	$scope.isLock = false;
}

//添加用户操作
$scope.addUser = function(user){
	var addUser = {
		id: user.id,
		name: user.name,
		age: user.age,
		gender: user.gender,
		job: user.job
	};
	User.post(addUser).$promise.then(function(res){
		if(res.status){
			alert('添加用户成功');
			$window.location.reload();
		}else{
			alert('添加用户失败');
		}
	});
}

运行效果:

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

这里在添加时会出现以下跨域错误提示:

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

要解决这个错误,在这里需要两个操作:

首先:在控制器中加入请求头配置

.config(function ($httpProvider, $httpParamSerializerJQLikeProvider){
  $httpProvider.defaults.transformRequest.unshift($httpParamSerializerJQLikeProvider.$get());
  $httpProvider.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  $httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
})

其次:在提供API的程序入口JS文件(nodexpressmongo/app.js)中添加跨域允许配置

//解决跨域访问
app.all('*', function(req, res, next){
	res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By",' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    res.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
    next();
});

此时重新请求,即可添加成功。

运行效果:

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

(5)修改用户数据

修改用户数据包括两部分内容:一是当点击某条记录的“修改”按钮时,通过查找单个用户信息服务获取到该用户信息,并回填到表单中,当用户输入修改后的信息后,点击“修改”按钮,提交修改数据。这一整个过程中,还有一些控制页面元素显示、隐藏或锁定的操作。

$scope.updateUser = function(userid){
	var updateBox = confirm('是否确定修改此用户信息?');
	if(updateBox==true){
		GetUser.get({id: userid}).$promise.then(function(res){
			if(res.status){
				$scope.user = res.data;
				$scope.showAddBtn = false;
				$scope.showUpdateBtn = true;
				$scope.showUserForm = true;
				$scope.isLock = true;
			}
		})
	}
}

$scope.handleUpdateUser = function(user){
	console.log(user);
	var updateUser = {
		_id: user._id,
		id: user.id,
		name: user.name,
		age: user.age,
		gender: user.gender,
		job: user.job
	};
	User.update(updateUser).$promise.then(function(res){
		if(res.status){
			alert('用户信息修改成功');
			$window.location.reload();
		}else{
			alert('用户信息修改成功');
		}
	});
}

运行效果:

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

使用AngularJS调用Node.js+Express+MongoDB RESTful接口实例

(6)删除用户数据

$scope.deleteUser = function(userid){
	var deleteBox = confirm('是否确定删除此用户信息?');
	if(deleteBox==true){
		GetUser.delete({id: userid}).$promise.then(function(res){
			console.log(res);
			if(res.status){
				alert('用户删除成功');
				$window.location.reload();
			}else{
				alert('用户删除失败');
			}
		});
	}
}

正如前面所述,删除用户数据使用与获取单个用户数据相同的服务,通过网址传ID参数给后端。而因为我们本系列第三篇文章《使用Node.js + Express + MongoDB整合构建RESTful Api》中并没有给带有参数的路由定义相应的delete方法,因此,我们还需要回到后端,为带有参数的路由定义delete方法,同时,在路由控制器文件中新增一个删除数据的回调函数,并指定给上面的方法。这里有两处修改:

修改一

将nodexpressmongo/routes/userRouter.js中的

userRouter.route('/:id')
	.get(userController.handleGetUser)

改为:

userRouter.route('/:id')
	.get(userController.handleGetUser)
	.delete(userController.handleDeleteUser);

修改二:

为nodexpressmongo/controllers/userController.js中userController对象增加一个方法:

handleDeleteUser: function(req, res){
	if(req.method === 'DELETE'){
		if(req.params && req.params.id){
			User.findById(req.params.id, function(err, user){
				if(err || !user){
					res.send(output(false, null, '查询不到数据'));
				}else{
					user.remove(function(err){
						if(err){
							res.send(output(false, null, '用户删除失败'));
						}else{
							User.find(function(err, users){
								if(err){
									res.send(output(false, null, '用户删除成功'));
								}else{
									res.send(output(true, users, '用户删除成功'));
								}
							});
						}
					})
				}
			});
		}else{
			res.send(output(false, null, '请求错误'));
		}
	}
}

app.js完整代码:

angular.module('myApp', ['ngResource'])

.constant('API_URL', 'http://localhost:3000/user')

// 加入此配置,可以传json数据
.config(function ($httpProvider, $httpParamSerializerJQLikeProvider){
  $httpProvider.defaults.transformRequest.unshift($httpParamSerializerJQLikeProvider.$get());
  $httpProvider.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  $httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
})

.factory('User', function($resource, API_URL){
	return $resource(API_URL, {},{
		'get': {method: 'GET'},
		'post': {method:'POST'},
		'update': {method: 'PUT'}
	});	
})

.factory('GetUser', function($resource, API_URL){
	return $resource(API_URL + '/:id', {id: '@id'},{
		'get': {method: 'GET'},
		'delete': {method: 'DELETE'}
	});	
})

.controller('indexCtrl', ['$scope', '$http', '$window', 'API_URL', 'User', 'GetUser', function($scope, $http, $window, API_URL, User, GetUser){
	$scope.showUserForm = false;	//是否显示表单
	$scope.showAddBtn = true;		//是否显示添加按钮
	$scope.showUpdateBtn = false;	//是否显示修改更新按钮
	$scope.users = [];				//用户列表数组
	$scope.user = {};				//单个用户数据
	$scope.isLock = false;			//是否锁定,一个状态,修改用户信息时不能修改ID

	//获取所有用户数据
	User.get({}).$promise.then(function(res){
		if(res.status){
			$scope.users = res.data;
		}
	});

	//添加用户按钮响应
	$scope.showAddUser = function(){
		$scope.showAddBtn = true;
		$scope.showUpdateBtn = false;
		$scope.showUserForm = true;
		$scope.isLock = false;
	}
	//添加用户操作
	$scope.addUser = function(user){
		var addUser = {
			id: user.id,
			name: user.name,
			age: user.age,
			gender: user.gender,
			job: user.job
		};
		User.post(addUser).$promise.then(function(res){
			if(res.status){
				alert('添加用户成功');
				$window.location.reload();
			}else{
				alert('添加用户失败');
			}
		});
	}

	//修改用户按钮
	$scope.updateUser = function(userid){
		var updateBox = confirm('是否确定修改此用户信息?');
		if(updateBox==true){
			GetUser.get({id: userid}).$promise.then(function(res){
				if(res.status){
					$scope.user = res.data;
					$scope.showAddBtn = false;
					$scope.showUpdateBtn = true;
					$scope.showUserForm = true;
					$scope.isLock = true;
				}
			})
		}
	}
	//修改用户操作
	$scope.handleUpdateUser = function(user){
		var updateUser = {
			_id: user._id,
			id: user.id,
			name: user.name,
			age: user.age,
			gender: user.gender,
			job: user.job
		};
		User.update(updateUser).$promise.then(function(res){
			if(res.status){
				alert('用户信息修改成功');
				$window.location.reload();
			}else{
				alert('用户信息修改失败');
			}
		});
	}

	//删除用户
	$scope.deleteUser = function(userid){
		var deleteBox = confirm('是否确定删除此用户信息?');
		if(deleteBox==true){
			GetUser.delete({id: userid}).$promise.then(function(res){
				console.log(res);
				if(res.status){
					alert('用户删除成功');
					$window.location.reload();
				}else{
					alert('用户删除失败');
				}
			});
		}
	}
}]);

至此,我们已经完成了这个简单的程序,使用AngularJS和ngResource来使用由Node.js、Express和MongoDB构建的RESTful API。程序比较简单,但基本上已覆盖到大部分知识点。

 

结语

Node.js开启了新的革命,Node.js结合Express和MongoDB的完美搭配让前端开发之路更宽更广。此系列文章作为引导入门之作,也是自己学习过程中的记录吧。本系列关于使用Node.js、Express、MongoDB和AngularJS来构建应用程序的文章到此结束。

那时那我

随遇,随缘,随安,随喜!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.