本篇文章主要參考自DreamersLab - 用Express和MongoDB寫一個todo list。原文的教學內容是使用Express 3.x版,在這邊做簡單Express 4.12.1的todo list實作。

功能介紹

  • 用cookie來判斷使用者
  • 新增、讀取、更新、刪除待辦事項(C.R.U.D)

安裝

開發環境

確定已安裝node.js, ExpressMongoDB

  • 安裝Express 4.12.1

    npm install express@4.12.1
  • 我們使用Mongoose來當作我們的ORM

步驟

用Express command line tool來建立基本框架

Express預設的template engine為jade,在這範例我們採用ejs

$ express -e todo
create : todo
create : todo/package.json
create : todo/app.js
create : todo/public
create : todo/public/javascripts
create : todo/public/images
create : todo/public/stylesheets
create : todo/public/stylesheets/style.css
create : todo/routes
create : todo/routes/index.js
create : todo/routes/users.js
create : todo/views
create : todo/views/index.ejs
create : todo/views/error.ejs
create : todo/bin
create : todo/bin/www install dependencies:
$ cd todo && npm install run the app:
$ DEBUG=todo:* ./bin/www
ejs-locals以及mongoose加入dependencies

package.json

{
"name": "todo",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.12.0",
"cookie-parser": "~1.3.4",
"debug": "~2.1.1",
"ejs": "~2.3.1",
"express": "~4.12.2",
"morgan": "~1.5.1",
"serve-favicon": "~2.2.0",
"ejs-locals" : "1.0.2",
"mongoose" : "3.8.17",
"uid-safe" : "1.1.0"
}
}
安裝dependencies
$ cd todo && npm install
Hello Wolrd!

開啟Express server然後打開瀏覽器瀏覽127.0.0.1:3000就會看到歡迎頁面

$ DEBUG=todo ./bin/www
檔案結構

此時專案的檔案結構大致為下:

todo
|-- node_modules
| |-- ejs
| |-- ejs-locals
| |-- express
| `-- mongoose
|
|-- public
| |-- images
| |-- javascripts
| `-- stylesheets
| |-- style.css
|
|-- routes
| `-- index.js
|
|-- views
| |-- index.ejs
| `-- layout.ejs
|
|-- .gitignore
|
|-- app.js
|
`-- package.json
  • node_modules
    • 包含所有project相關套件
  • public
    • 包含所有靜態檔案
  • routes
    • 所有動作及商業邏輯
  • views
    • 包含action views, partials及layout
  • app.js
    • 包含設定、middlewares以及routes的分配
  • package.json
    • 相關套件的設定檔
MongoDB以及Mongoose設定

Ubuntu上MongoDB開機便會自動開啟,若是Mac上就輸入下面指令開啟

$ mongod

並且在專案根目錄之下新增一個檔案叫做db.js來設定MongoDB和定義Schema

db.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema; var Todo = new Schema({
user_id : String,
content : String,
updated_at : Date
}); mongoose.model('Todo', Todo);
mongoose.connect('mongodb://localhost/express-todo');

並且在app.js裡require剛剛新增的db.js

app.js

require('./db');

並且移除掉Express預設產生,我們用不到的user route,並且加上layout support

var engine = require('/ejs-locals');
app.engine('ejs', engine);

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser'); var routes = require('./routes/index'); var app = express();
var engine = require('/ejs-locals'); // view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('ejs', engine); // uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
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('/', routes); // catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
}); // error handlers // development error handler
// will print stacktrace
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
});
});
} // production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
}); module.exports = app;
修改project title

routes/index.js

原本的預設產生的程式碼如下:

var express = require('express');
var router = express.Router(); /* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
}); module.exports = router;

在這個範例之中我們一律將router的分配寫在app.js當中,因此將程式碼修改為:

var express = require('express');

exports.index = function(req, res) {
res.render('index', {title : 'Express Todo Example'});
};
修改index view

在這裡我們需要一個textbox來新增待辦事項,並且用POST來傳送資料,另外別忘了設定layout

views/index.ejs

<% layout( 'layout' ) -%>

<h1><%= title %></h1>
<form ac 大专栏  用Express 4和 MongoDB打造Todo Listtion="/create" method="POST" accept-charset="utf-8">
<input type="text" name="content" />
</form>
新增待辦事項及存檔

routes/index.js

首先先require mongooseTodo model

var mongoose = require('mongoose');
var Todo = mongoose.model('Todo'); //新增成功後將頁面導回首頁.
exports.create = function(req, res) {
new Todo({
content : req.body.content,
updated_at : Date.now()
}).save(function(err, todo, count) {
res.redirect( '/' );
});
};

接著在app.js當中新增對應的route

app.js

// 新增下列語法到 routes
app.post('/create', routes.create);
顯示待辦事項

routes/index.js

// 查詢資料庫來取得所有待辦是事項.
exports.index = function(req, res) {
Todo.find( function(err, todos, count) {
res.render('index', {
title : 'Express Todo Example',
todos : todos
});
});
};

views/index.ejs

// 跑迴圈顯示待辦事項
<% todos.forEach(function(todo) { %>
<p><%= todo.content %></p>
<% }); %>
刪除待辦事項

在每一個待辦事項旁邊增加刪除的連結

routes/index.js

// 根據待辦事項的id來做移除
exports.destroy = function(req, res) {
Todo.findById(req.params.id, function(err, todo) {
todo.remove( function(err, todo) {
res.redirect( '/' );
});
});
};

views/index.ejs

// 在迴圈裡加一個删除連結
<% todos.forEach(function(todo) { %>
<p>
<span>
<%= todo.content %>
</span>
<span>
<a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
</span>
</p>
<% }); %>

將destroy的動作新增到對應的route

app.js

// 新增下列語法到 routes
app.get('/destroy/:id', routes.destroy);
編輯待辦事項

當滑鼠點擊事項時,將它轉為一個text input達到編輯效果

routes/index.js

exports.edit = function(req, res) {
Todo.find(function(err, todos) {
res.render('edit', {
title : 'Express Todo Example',
todos : todos,
current : req.params.id
});
});
};

新增Edit view,基本上和index view差不多,唯一不同是在被選取的待辦事項變成text input

view/edit.js

<% layout( 'layout' ) -%>

<h1><%= title %></h1>
<form action="/create" method="post" accept-charset="utf-8">
<input type="text" name="content" />
</form> <% todos.forEach(function(todo) { %>
<p>
<span>
<% if ( todo._id == current ) { %>
<form action="/update/<%= todo._id %>" method="POST" accept-charset="utf-8">
<input type="text" name="content" value="<%= todo.content %>" />
</form>
<% } else { %>
<a href="/edit/<%= todo._id %>" title="Update this todo item"><%= todo.content %></a>
<% } %>
</code>
<span>
<a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
</code>
</p>
<% }); %>

待辦事項新增可以連到edit的link

views/index.ejs

<% layout( 'layout' ) -%>

<h1><%= title %></h1>
<form action="/create" method="post" accept-charset="utf-8">
<input type="text" name="content" />
</form> <% todos.forEach(function(todo) { %>
<p>
<span>
<a href="/edit/<%= todo._id %>" title="Update this todo item"><%= todo.content %></a>
</code>
<span>
<a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
</code>
</p>
<% }); %>

將連結到編輯的route新增到app.js中

app.js

app.get('/edit/:id', routes.edit);
更新待辦事項

新增update動作來更新待辦事項

routes/index.js

exports.update = function(req, res) {
Todo.findById(req.params.id, function(err, todo) {
todo.content = req.body.content;
todo.updated_at = Date.now();
todo.save(function(err, todo, count) {
res.redirect( '/' );
});
});
};

將更新的動作新增到routes

app.js

app.post('/update/:id', routes.update);
排序

現在待辦事項的順序為:較早創建/更新的在上面,我們要將它相反

routes/index.js

exports.index = function(req, res) {
Todo.
find().
sort('-updated_at').
exec(function(err, todos) {
res.render('index', {
title : 'Express Todo Example',
todos : todos
});
});
}; exports.edit = function(req, res) {
Todo.
find().
sort('-updated_at' ).
exec(function(err, todos) {
res.render('edit', {
title : 'Express Todo Example',
todos : todos,
current : req.params.id
});
});
};
多重使用者

目前為止,所有使用者看到的都是同一組待辦事項,資料有可能會被外人所修改,因此,我們可利用cookie來記錄使用者資訊,讓每個人都有自己的todo list。
Express已經有內建的cookie,我們先在app.js當中新增一個middleware就好。

app.js

// 將抓取使用者資訊的middleware加入app.js
app.use( routes.current_user );

接著在routes/index.js增加current_user的運作邏輯

routes/index.js

// 我們採用uid-safe package來替我們產生uid,別忘了要npm install uid-safe哦
var uid = require('uid-safe'); exports.current_user = function(req, res, next) {
var user_id = req.cookies ?
req.cookies.user_id : undefined;
if ( ! user_id ) {
uid(32).then(function(uid) {
res.cookie('user_id', uid);
});
}
next();
};
Error handling

要處理錯誤我們需要新增next參數到每個action當中,一旦發生錯誤便將他傳給下一個middleware去做處理

routes/index.js

... function ( req, res, next ){
// ...
}; ...( function( err, todo, count ){
if( err ) return next( err ); // ...
});
最後一步,執行
$ DEBUG=todo ./bin/www

用Express 4和 MongoDB打造Todo List的更多相关文章

  1. vue.js+socket.io+express+mongodb打造在线聊天

    vue.js+socket.io+express+mongodb打造在线聊天 在线地址观看 http://www.chenleiming.com github地址 https://github.com ...

  2. vue.js+socket.io+express+mongodb打造在线聊天[二]

    vue.js+socket.io+express+mongodb打造在线聊天[二] 在线地址观看 http://www.chenleiming.com github地址 https://github. ...

  3. 基于MongoDB打造.Net的分布式Session子系统

    基于MongoDB打造.Net的分布式Session子系统 Taobao有她自己的分布式session框架,.net阵营也不能落后了,在下做了个基于MongoDB的支持最多26台MongoDB的分布式 ...

  4. Express中使用mongodb存储session

    express默认有队session的支持,但是是存储在内存中的. 我们可以使用mongodb来存储会话. 但是express的各个版本中对该功能的写法是不同的. Express 2.x: app.u ...

  5. 【Node.js】二、基于Express框架 + 连接MongoDB + 写后端接口

    在上节,我们讲了如何搭建express环境,现在我们说说如何通过node.js写服务接口给前端调用 1. 首先通过MongoDB建好数据库与表格 例如,我的数据库名字为db_demo,数据库表格为go ...

  6. nodejs之使用express框架连接mongodb数据库

    var express = require('express');var router = express.Router();var app = express();var MongoClient = ...

  7. 使用Golang+Mongodb打造你的第一个站点

    很多人推荐MEAN来开发站点.MEAN就是M:mongodb,E:expressjs.A:angular最后的N:nodejs. 但是如果你亲身的体会到了js的嵌套回调的话你就会想换换别的办法了.虽然 ...

  8. NODE 基于express 框架和mongoDB的cookie和session认证 和图片的上传和删除

    源码地址 https://gitee.com/zyqwasd/mongdbSession 本项目的mongodb是本地的mongodb 开启方法可以百度一下 端口是默认的27017 页面效果 1. 注 ...

  9. 基于Express+Socket.io+MongoDB的即时聊天系统的设计与实现

    记得从高中上课时经常偷偷的和同学们使用qq进行聊天,那时候经常需要进行下载qq,但是当时又没有那么多的流量进行下载,这就是一个很尴尬的事情了,当时就多想要有一个可以进行线上聊天的网站呀,不用每次痛苦的 ...

随机推荐

  1. Android studio 3.0安装与配置(看这一篇就够了)

    前言 为了完成数据库大作业,并充分利用学过的Java语言,决定开发一个简单完整成熟的安卓手机应用程序.于是下载安装Android Studio集成开发环境,第一次安装最新版本,因为墙的原因安装失败,第 ...

  2. React Native 开发

    摘自:<React Native 开发之 IDE 选型和配置> 一个在不断更新的有关React Native讲解:<江清清的技术专栏> ES5和ES6的区别:<React ...

  3. Ioc和依赖注入

     转自https://www.cnblogs.com/zhangzonghua/p/8540701.html 1.IOC 是什么 IOC- Inversion of Control , 即“控制反转” ...

  4. 关于volatile关键字

    来源:衡阳网站优化 在java核心卷1中对volatile关键字是这么描述的: volatile关键字为实例域的同步访问提供了一种免锁机制.如果声明一个域为volatile,那么编译器和虚拟机就知道该 ...

  5. Java操作redis客户端Jedis连接集群(Cluster)

    创建JedisCluster类连接redis集群. @Test public void testJedisCluster() throws Exception { //创建一连接,JedisClust ...

  6. iOS 清理文件缓存

    本文摘自:<msp的昌伟哥哥-iOS开发-清理缓存功能的实现>摘下来的目的就是为了能够学习.还望看到文章的同学,前往原创的博客园.感谢msp的昌伟哥哥的分享精神. 移动应用在处理网络资源时 ...

  7. 基础篇九:模块介绍(--with-http_stub_status_module)

    下面--with 即为编译安装的模块 下面我们来介绍--with-http_stub_status_module此模块 vim  /etc/nginx/conf.d/default.conf 然后检查 ...

  8. Super Mario HDU - 4417 (主席树询问区间比k小的个数)

    Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory ...

  9. Linear Equations

    4.1 Linear Equations with One Independent Variable

  10. 线程中调用service方法出错

    public class PnFileTGIComputeThread implements Runnable { @Resource private AppUsedService appUsedSe ...