转自 http://blog.csdn.net/blacksiders/article/details/50081941
基于Nodejs+express4+Mongodb+Angularjs建立web项目
– 基于Nodejs搭建web服务器
– 利用Express4搭建restful服务
– 使用Mongodb作为数据库,用mongoose组件连接Mongodb
– 使用AngularJS+bootstrap设计UI界面
– 使用Webstorm 10.0.4开发
第一部分:服务端搭建
1.通过Webstorm建立Node.js Express APP
File -> New Project -> Node.js Express App,
Location栏填写项目名称,选择Nodejs和npm位置,
Options -> Template 选择EJS
在项目下新建文件夹models保存mongodb相关对应model。
在models目录下创建model文件movie.js。
在routes目录下创建restful API 支持文件 movies.js
完成后的目录结构如下
- project
-- bin
--- www
-- models
--- movie.js
-- node_modules
--- ...
-- public
--- ...
-- routes
movies.js
app.js
package.json
2.在package.json中添加mongoose支持及其他相关组件
{
"name": "project",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.13.2",
"cookie-parser": "~1.3.5",
"debug": "~2.2.0",
"ejs": "~2.3.3",
"express": "~4.13.1",
"morgan": "~1.6.1",
"errorhandler": "~1.4.2",
"serve-favicon": "~2.3.0",
"mongoose": "~4.2.5",
"connect-mongo": "latest",
"express-session": "latest"
},
"engines": {
"node": ">=0.10.0"
}
}
3.在movie.js中建立movie模型:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// define an db object
var movieSchema = new Schema({
title: String,
releaseYear: String,
director: String,
genre: String
});
// bind module for accessing outside
module.exports = mongoose.model('Movie', movieSchema);
4.在movies.js中建立基于movie的restful API
引入movie模型,并开启router
var Movie = require('../models/movie.js');
var express = require('express');
var router = express.Router();
实现一些基本的功能API
var Movie = require('../models/movie.js');
// get all movies
exports.list = function(req, res){
Movie.find(function (err, movies) {
if (err) {
return res.send(err);
}
res.json(movies);
})
};
// create new movie
exports.create = function(req, res){
var movie = new Movie(req.body);
movie.save(function (err) {
if (err) {
return res.send(err);
}
res.send({message: 'add a movie'});
});
};
// update a movie
exports.update = function (req, res) {
//Movie.findById(req.params.id, callback)
Movie.findOne({_id: req.params.id}, function (err, movie) {
if (err) {
return res.send(err);
}
for (prop in req.body) {
movie[prop] = req.body[prop];
}
movie.save(function (err) {
if (err) {
return res.send(err);
}
res.json({message: "update a movie"});
});
});
};
//delete a movie
exports.delete = function (req, res) {
Movie.remove({_id: req.params.id}, function (err, movie) {
if (err) {
return res.send(err);
}
res.json({message: 'delete a movie'});
});
};
导出功能模块让其他部分可以使用该模块
module.exports = router;
5.启用Restful服务
在app.js中引入mongoose建立数据库连接
var mongoose = require('mongoose');
// connect to mongodb
var dbName = 'movie';
var url = 'mongodb://localhost:27017/' + dbName;
var mongoOptions = {
server: {
socketOptions: {
keepAlive: 1
}
}
};
mongoose.connect(url, mongoOptions);
mongoose.connection.on('error', function (err) {
console.log('Mongo Error:' + err);
}).on('open', function () {
console.log('Connection opened');
});
引入router启用Restful服务
var express = require('express');
var app = express();
var movies = require('./routes/movies');
app.use('/api', movies);
由于http通信是基于json格式的,需要指定请求数据格式和编码
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
导出模块
module.exports = app;
6.在bin/www中开启server
var app = require('../app');
var http = require('http');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
//Create HTTP server
var server = http.createServer(app);
server.listen(port);
第二部分:前端搭建
前端使用AngularJS+Bootstrap搭建
1.引入bower
先安装NodeJS,使用npm工具引入bower。
在项目文件夹pubic下依次建立以下目录结构
- project
-- ...
-- public
--- css
--- images
--- js
---- controller
---- service
--- views
index.html
添加bower依赖,在项目根目录下新建.bowerrc文件,指定bower安装路径
{
"directory": "/public/bower_components"
}
添加bower.json文件指定依赖:
{
"name": "project",
"version": "0.0.0",
"dependencies": {
"jquery": "~2.1.3",
"bootstrap": "~3.3.2",
"angular": "~1.3.8",
"font-awesome": "~4.2.0",
"angular-ui-router": "~0.2.13",
"angular-bootstrap": "~0.12.1"
}
}
运行bower install,安装相关js插件
2.引入AngularJS组件
在public > js下新建module.js,创建AngularJS项目组件app
'use strict';
var app = angular.module('app', [
'ui.router',
'ui.bootstrap'
]);
创建一个service来保存windows session
session.storage.js
(function (app) {
'use strict';
app.factory('SessionStorage', function ($window) {
var store = $window.sessionStorage;
return { save: function (key, value) { value = angular.toJson(value); store.setItem(key, value); }, get: function (key) { var value = store.getItem(key); if (value) { value = angular.fromJson(value); } return value; }, delete: function (key) { store.removeItem(key); } }
});
})
(angular.module('app'));
新建一个movie service进行http请求
movie.service.js
(function (app) {
'use strict';
app.factory('MovieService', function ($http, $q) {
return {
getAllMovies: function () {
var url = "http://localhost:3000/api/movie/all";
var deferred = $q.defer();
$http.get(url).then(
function success(respData) {
var movies = respData.data;
deferred.resolve(movies);
},
function error(reason) {
deferred.reject(reason);
}
);
return deferred.promise;
},
updateMovie: function (movie, id) {
var url = "http://localhost:3000/api/movie/" + id;
var deferred = $q.defer();
$http.put(url, movie).then(
function success(respData) {
var movies = respData.data;
deferred.resolve(movies);
},
function error(reason) {
deferred.reject(reason);
}
);
return deferred.promise;
}
}
});
})(angular.module('app'));
在controller目录下建立两个controller处理相关数据操作
main.controller.js
(function (app) {
'use strict';
app.controller('MainController', function ($scope, $rootScope, $state, SessionStorage, movies) {
$rootScope.title = 'express_demo2';
$scope.movies = movies;
$scope.updateMovie = function (movie) {
SessionStorage.delete('movie');
$state.go('movie.update', {data: movie});
};
});
})(angular.module('app'));
movie.controller.js
(function (app) {
'use strict';
app.controller('MovieController', function ($scope, $rootScope, $state, $stateParams, MovieService, SessionStorage) {
$scope.movie = $stateParams.data;
if (!$scope.movie) { $scope.movie = SessionStorage.get('movie'); } else { SessionStorage.save('movie', $scope.movie); }
$scope.update = function () { var promise = MovieService.updateMovie($scope.movie, $scope.movie._id); promise.then(function (data) { alert('update success!'); SessionStorage.delete('movie'); $state.go('movie.main'); }); };
});
})(angular.module('app'));
建立uirouter指定路由
(function (app) {
'use strict';
app.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/movie');
$stateProvider
.state('movie', {
abstract: 'true',
templateUrl: '/views/menu.html'
})
.state('movie.main', {
url: '/movie',
controller: 'MainController',
templateUrl: '/views/main.html',
resolve: {
'movies': function (MovieService) {
return MovieService.getAllMovies();
}
}
})
.state('movie.update', {
url: '/movie/update',
controller: 'MovieController',
templateUrl: '/views/update.html',
params: {
data: null
}
});
});
})(angular.module('app'));
在css文件下建立css
.margin-top-20 {
margin-top: 20px;
}
.title-font {
font-size: large;
font-style: italic;
font-family: Consolas;
font-weight: bold;
}
在index.html中声明app位置,并导入相关的js和css文件
<html ng-app="app">
<head>
<meta charset="UTF-8"/>
<base href='/'/>
<title ng-bind="title">title>
<link rel='stylesheet' href='bower_components/bootstrap/dist/css/bootstrap.css'/>
<link rel='stylesheet' href='bower_components/font-awesome/css/font-awesome.css'/>
<link rel='stylesheet' href='css/style.css'/>
head>
<body>
<div ui-view>div>
<script src="bower_components/jquery/dist/jquery.min.js">script>
<script src="bower_components/angular/angular.min.js">script>
<script src="bower_components/bootstrap/dist/js/bootstrap.min.js">script>
<script src="bower_components/angular-ui-router/release/angular-ui-router.min.js">script>
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js">script>
<script src="js/module.js">script>
<script src="js/routes.js">script>
<script src="js/controller/main.controller.js">script>
<script src="js/controller/movie.controller.js">script>
<script src="js/service/movie.service.js">script>
<script src="js/service/session.storage.js">script>
body>
html>
在views目录下建立三个html页面进行操作
main.html
<table class="table table-bordered">
<thead>
<tr>
<th>Titleth>
<th>Yearth>
<th>Directorth>
<th>Genreth>
<th>Optionth>
tr>
thead>
<tbody>
<tr ng-repeat="movie in movies">
<td>{{movie.title}}td>
<td>{{movie.releaseYear}}td>
<td>{{movie.director}}td>
<td>{{movie.genre}}td>
<td><a class="btn btn-default" href="javascript:void(0)" ng-click="updateMovie(movie)">Updatea>td>
tr>
tbody>
table>
menu.html
<div class="container">
<div class="row margin-top-20">
class="title-font">Movie Book System
div>
div>
<div class="container">
<div class="row">
<div class="ui-view">div>
div>
div>
update.html
在app.js中声明html位置
app.set('views', path.join(__dirname, 'public'));
app.engine('html', require('ejs').__express);
app.set('view engine', 'html');
// rewrite to load static resources
app.use(express.static(path.join(__dirname, 'public')));
// static views
app.all('/*', function (req, res) {
res.sendfile('index.html', {root: path.join(__dirname, 'public')});
});
至此,运行www文件即可启动nodejs项目,在界面上访问localhost:3000即可访问项目。
=========================================
实现过程中遇到的异常处理:
(1) 在用bower安装依赖的时候,出现了Bower : ENOGIT git is not installed or not in the PATH ,这个错误提示很明显。就是git没有设置在环境变量中!
解决方法一:
添加git到window的环境变量中。设置path路径为C:\Program Files\Git\bin
关于环境变量的设置,在这里就不多说了。
解决方法二:
在不用添加环境变量的情况下,运行下面一个命令,就可以设置当前文件加的环境变量。
$ set PATH=%PATH%;C:\Program Files\Git\bin
运行完了上面的命令,然后你再用bower安装依赖就成功了,不会有Bower : ENOGIT git is not installed or not in the PATH 这个报错了!
(2) 执行bower install命令遇到异常:
ECONFLICT Unable to find suitable version for angular
解决方法:
It worked for me by making following changes in "bower.json":
"angular": "latest",
"angular-mocks": "latest",
"jquery": "latest",
"bootstrap": "latest"
(3)查询movie列表时查询结果总为空
解决方法:将movie.js中
module.exports = mongoose.model('Movie',movieSchema);
改为
module.exports = mongoose.model('Movie',movieSchema,'Movie');
原因:
使用Mongoose#model(name, [schema], [collection], [skipInit]);定义模型, mongoose 会把表名变成复数 所以这里collection参数要写上
整个工程流程架构:
views文件夹下定义视图,routes.js中定义路由,路由将视图和控制器Controller进行关联,controller调用service完成业务数据操作,之后将操作结果反映在视图中(例如通过$scope.movies=movies
实现变量双向绑定,通过$scope.updateMovie
和视图中的ng-click
关联)。而service通过调用模型中的API实现数据库操作。