前言 经过前面那篇文章的准备,现在已经搭建好MEAN架构的开发环境了。这篇文章将介绍如何实现TODO的项目。 创建一个TODO项目的步骤如下:
创建一个Express工程项目
创建一个SPA,用于前台创建TODO事项,编辑和删除TODO事项以及显示TODO列表
保存TODO到数据库(本文采用Mongoose)
使用Node.js创建REST API
使用AngularJS作为前端调用REST API
Github地址:https://github.com/superman66/node-express-todo 项目效果图:
Express项目结构 在第一篇文章,我们已经创建了一个名字为todo的Express项目了,创建成功后的目录结构如下: 接下来就介绍下express项目下的文件夹的作用
node_modules:通过npm安装依赖包所存放的路径;
public:存在公用的文件如CSS文件,图片文件等;
routers:项目路由文件夹,项目所有的路由文件都放在该文件夹下;
views:页面文件,可以是html文件,也可以是模板引擎文件;
app.js:app.js是项目的主要入口文件;
db.js:db.js存放着数据库连接信息,是自己创建的,具体后面会介绍。
TODO项目开发 介绍了Express项目的文件夹结构之后,就可以开始进入代码开发了。
安装依赖包 在刚创建Express项目后,我们已经通过npm install
安装了必要的依赖。但这还不够,我们还需要额外安装以下的npm:
使用以下命令,即可安装:
1 npm install mongoose angular
我们使用的是MongoDB作为数据库,由于直接使用MongoDB操作连接数据库一点都不方便,因为在这里我们采用了mongoose
。
Mongoose是基于node-mongodb-native开发的MongoDB nodejs驱动,可以在异步的环境下执行。Mongoose和MondoDB的关系就好比jQuery和原生JavaScript。使用Mongoose操作数据数据库更加方便 。
项目开发步骤 第一步:创建Express工程 这一步我们已经完成了。
第二步:后台配置 这一步包括数据库配置,静态文件夹服务器配置以及路由配置。 *数据库配置
首先在根目录下创建一个db.js
,用于存放数据库连接信息。我们还需要创建一个Todo Model
,该model有id,content,update_at三个属性。在mongoose中,model是可以操作数据库的。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var mongoose = require ('mongoose' );var Schema = mongoose.Schema;var Todo = new Schema({ id : String , content : String , update_at : Date }); mongoose.model('Todo' , Todo); mongoose.connect('mongodb://localhost/express-todo' );
接下来在app.js
顶部中添加引用
1 var db = require ('./db' );
Express项目默认public文件夹如静态文件的位置,我们也可以再添加文件夹作为静态文件服务器,代码如下:
1 2 app.use(express.static(path.join(__dirname, 'public' ))); app.use(express.static(path.join(__dirname, 'node_modules' )));
Express项目使用了路由中间件,将路由信息从app.js中剥离出来,单独放在router文件夹中。但是我们需要在app.js
中配置,代码如下:
完整的app.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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 var express = require ('express' );var db = require ('./db' );var path = require ('path' );var favicon = require ('serve-favicon' );var logger = require ('morgan' );var cookieParser = require ('cookie-parser' );var bodyParser = require ('body-parser' );var partials = require ('express-partials' );var routes = require ('./routes/index' );var users = require ('./routes/users' );var app = express();app.set('views' , path.join(__dirname, 'views' )); app.use(logger('dev' )); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended : false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public' ))); app.use(express.static(path.join(__dirname, 'node_modules' ))); app.use('/' , routes); app.use(function (req, res, next ) { var err = new Error ('Not Found' ); err.status = 404 ; next(err); }); if (app.get('env' ) === 'development' ) { app.use(function (err, req, res, next ) { res.status(err.status || 500 ); res.render('error' , { message: err.message, error: err }); }); } app.use(function (err, req, res, next ) { res.status(err.status || 500 ); res.render('error' , { message: err.message, error: {} }); }); module .exports = app;
第三步:创建REST API 路由 在创建之前,我们可以先规划下,我们都需要用到哪些API:
返回所有的todo列表: /api/todos
、GET
创建todo: /api/todo
、POST
更新todo:/api/todo/:id
、PUT
删除todo:/api/todo/:id
、DELETE
这个四个API的代码如下:
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 router.get('/api/todos' , function (req, res, next ) { Todo .find() .sort('-update_at' ) .exec(function (err, todos ) { if (err){ res.send(err); } res.json(todos); }); }); router.post('/api/todo' , function (req, res, next ) { new Todo({ content : req.body.content, update_at : new Date () }).save(function (err, todo, count ) { if (err){ res.send(err); } Todo .find() .sort('-update_at' ) .exec(function (err, todos ) { if (err){ res.send(err); } res.json(todos); }); }) }); router.delete('/api/todo/:id' , function (req, res, next ) { Todo.findById(req.params.id, function (err, todo ) { todo.remove(function (err, todo ) { if (err){ res.send(err); } Todo .find() .sort('-update_at' ) .exec(function (err, todos ) { if (err){ res.send(err); } res.json(todos); }); }) }) }); router.put('/api/todo/:id' , function (req, res, next ) { Todo.findById(req.params.id, function (err, todo ) { if (err){ res.send(err); } todo.content = req.body.content; todo.save(function (err ) { console .log('update success' ); }); }); Todo .find() .sort('-update_at' ) .exec(function (err, todos ) { if (err){ res.send(err); } res.json(todos); }); });
最后我们还需要在添加一个api,用于显示前端页面:
1 2 3 router.get('/' , function (req, res ) { res.sendfile('./views/index.html' ); });
第四步:创建AngularJS应用 新建index.module.js
,用于定于AngularJS应用,代码如下:
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 (function ( ) { 'use strict' ; angular.module('TodoApp' , []) .factory('TodoService' , ['$http' , function ($http ) { var _getTodoList = function ( ) { return $http.get('/api/todos' ); }; var _createTodo = function (todo ) { return $http({ method: 'POST' , url: '/api/todo' , data: todo }) }; var _deleteTodo = function (id ) { return $http({ method: 'DELETE' , url: '/api/todo/' + id }) }; var _updateTodo = function (todo ) { return $http({ method: 'PUT' , url: '/api/todo/' + todo._id, data: todo }) }; return { todoList: _getTodoList, createTodo: _createTodo, deleteTodo: _deleteTodo, updateTodo: _updateTodo } }]) .controller('TodoController' , ['TodoService' , function (TodoService ) { var vm = this ; vm.todos = []; vm.todo = { content: '' }; TodoService.todoList().success(function (data ) { vm.todos = data; }).error(function (err ) { console .log(err); }) vm.createTodo = function (todo ) { TodoService.createTodo(todo).success(function (data ) { vm.todos = data; vm.todo.content = '' ; }); }; vm.delTodo = function (id ) { TodoService.deleteTodo(id).success(function (data ) { vm.todos = data; }) }; vm.updateTodo = function (todo ) { TodoService.updateTodo(todo).success(function (data ) { }) } }]); })();
在这个文件中,我们把所有的http请求都放在了TodoService
中,然后在Controller中注入。这样就可以在Controller中使用了。
第五步:创建HTML页面 这里就不对AngularJS的语法做介绍了,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 28 29 30 31 32 33 34 35 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" > <title > TODO</title > <link rel ='stylesheet' href ='/stylesheets/style.css' > </head > <body ng-app ="TodoApp" > <div class ="container" ng-controller ="TodoController as vm" > <h1 class ="title" > TODO</h1 > <section class ="form-content" > <form ng-submit ="vm.createTodo(vm.todo)" > <input class ="form-control" type ="text" name ="content" ng-model ="vm.todo.content" placeholder ="添加待办事项" required /> </form > <ul class ="todo-list" > <li ng-repeat ="todo in vm.todos" > <a > <input type ="text" class ="form-control btn-primary" ng-model ="todo.content" ng-blur ="vm.updateTodo(todo)" /> </a > <a class ="btn-del" ng-click ="vm.delTodo(todo._id)" title ="Delete this todo item" > </a > </li > </ul > </section > </div > <footer > 输入回车键进行提交 </footer > </body > <script src ="/jquery/dist/jquery.min.js" > </script > <script src ="/angular/angular.min.js" > </script > <script src ="/index.module.js" > </script > </html >
到这里,一个基于MEAN的TODO项目就完成了。你可以运行npm start
运行项目,在浏览器中输入localhost:3000
就可以看到页面了。
总结 这个项目一开始只是想学习如何构建一个Express+Nodejs的项目,在完成之后开始思考,如何将其变成一个前后端分离的项目。想到我对AngularJS比较擅长,于是前端就采用AngularJS,代替了项目中自带的ejs模板引擎。然后这个技术栈就组成了简易型的MEAN架构了。突然想起来最开始学Express+Node项目的初衷就是为了掌握MEAN Stack,通过这么一个过程,虽然不能说已经掌握了MEAN Stack,但是已经对其有了结构性的了解,因为MEAN最基础的组成部分就是MongoDB
、Express
、AngularJS
、Nodejs
。对MEAN Stack有兴趣的,可以前往官网http://mean.io/了解更多。
参考资料:http://www.jdon.com/idea/nodejs/web-app-with-angularjs-and-rest-api-with-node.html http://dreamerslab.com/blog/en/write-a-todo-list-with-express-and-mongodb/ https://cnodejs.org/topic/504b4924e2b84515770103dd