Express+Mongodb三

Express+Mongodb+Ajax,实现博客增删查改

初始化项目

1
2
3
4
express blogDemo
cd blogDemo
cnpm install
npm start

修改模板引擎

为了能让 html 文件作为 express 渲染页面的模板,因此我们决定采用 ejs 模板引擎,因为它和html是相互兼容的。要使用它,先安装

1
cnpm install ejs --save

然后是在app.js里修改模板引擎,在根目录的app.js中,有关模板引擎的是这么两句

1
2
3
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

第一句:设置模板的路径是在根目录(__dirname)下的views文件夹
第二句:将模板引擎设置为以.jade为后缀名的文件
将上面两句修改为以下三句,即可

1
2
3
4
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.engine('html', require("ejs").__express);

新增页面

然后将 views 文件下的 jade 文件全部删除,并新增 index.html、update.html、error.html 三个html文件
index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>
<body>
<h3 class=""><%= name %></h3>
</body>
</html>

update.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>
<body>
<h3 class=""><%= name %></h3>
</body>
</html>

error.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>错误页</title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>
<body>
<div class="content">错误!</div>
</body>
</html>

设置路由

要使得以上3个页面能正常访问,需要在 routes 文件下设置路由。
先删除 routes 下的所有js文件,然后再新增 index.js 和 update.js 文件(这里无需error.js,因为在app.js里有写)
index.js

1
2
3
4
5
6
7
8
9
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: '博客首页', name: '博客'});
});
module.exports = router;

update.js

1
2
3
4
5
6
7
8
9
var express = require('express');
var router = express.Router();
/* GET update page. */
router.get('/update', function(req, res, next) {
res.render('update', { title: '博客更新页', name: '博客更新'});
});
module.exports = router;

我们在根目录的 app.js 里设置相应的路由

1
2
3
4
5
6
7
var routes = require('./routes/index');
var users = require('./routes/users');
...
app.use('/', routes);
app.use('/users', users);

修改为:

1
2
3
4
5
6
7
var index = require('./routes/index');
var update = require('./routes/update');
...
app.use('/', index);
app.use('/', update);

最后,检验下我们的成果。在项目根目录打开命令行面板,输入以下命令

1
npm start

在浏览器里打开 http://localhost:3000/,http://localhost:3000/update, 便可以看到博客首页、博客更新页。

http://localhost:3000/errorhttp://localhost:3000/abc 之类的则会定向到错误页

调整模板

我们接着调整index.html、update.html页面,以及public里stylesheets目录下的css文件,
将首页和更新页的UI界面调整成文章开头处的博客系统UI界面。并在public里javascripts目录下新增jquery.min.js文件
index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="blog-add">
<div class="blog-textarea">
<textarea name="" class="textarea-add" cols="30" rows="10"></textarea>
</div>
<p class="blog-action">
<a class="btn-publish" href="javascript:;">发布</a>
</p>
</div>
<div class="blog">
<ul class="blog-list">
<li class="blog-item">
<p class="blog-content">你好</p>
<p class="blog-extra">
<span class="date">2015-10-10</span>
<a href="javascript:;" class="delete">删除</a>
<a href="javascript:;" class="update">更新</a>
</p>
</li>
</ul>
</div>

update.html

1
2
3
4
5
6
7
8
<div class="blog-add">
<div class="blog-textarea">
<textarea name="" class="textarea-add" cols="30" rows="10"></textarea>
</div>
<p class="blog-action">
<a class="btn-update" href="javascript:;">更新</a>
</p>
</div>

调整后的css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
* {margin: 0;padding: 0;}
body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
a {color: #00B7FF;}
li {list-style: none;}
a {text-decoration: none;}
.blog-add .blog-textarea {margin: 20px 0 10px;padding: 10px;border: 1px solid #ccc;}
.blog-add .textarea-add {width: 100%;height: 80px;border: none;resize: none;outline: none;}
.blog-add .blog-action {line-height: 30px;text-align: right;}
.blog-add .blog-action .btn-publish,
.blog-add .blog-action .btn-update {border: 1px solid #00B7FF;padding: 5px 20px;border-radius: 3px;}
.blog-list .blog-item {padding-top: 10px;line-height: 22px;}
.blog-list .blog-item p {padding: 5px 0;word-wrap: break-word;}
.blog-list .blog-item {border-bottom: 1px dotted #ddd;}
.blog-list .blog-extra {overflow: hidden;color: #999;text-align: right;}
.blog-list .blog-extra .date {float: left;}
.blog-list .blog-extra a {border-right: 1px dotted #00B7FF;padding: 0 10px;margin-right: -6px;font-size: 12px;}

express与mongodb交互

交互流程,在首页中,我们点击 ‘发布’ 按钮,将文本框里的内容发送至后台,后台语言将数据保存到数据库,
然后页面刷新,从数据库中读取博客数据,最后,将数据显示到页面

发送数据至后台

index.html 新增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
<script type="text/javascript" src="/javascripts/jquery.min.js"></script>
<script>
$(function() {
// 发布
$('.btn-publish').on('click', function() {
var blogContent = $('.textarea-add').val().trim().replace(/\n/g, '<br/>');
if (!blogContent) {
alert('内容不能为空!');
return;
}
var date = new Date(),
yy = date.getFullYear(),
MM = date.getMonth() + 1,
dd = date.getDate(),
hh = date.getHours(),
mm = date.getMinutes(),
ss = date.getSeconds();
var postData = {
'content': blogContent,
'date': yy + '-' + MM + '-' + dd + ' ' + hh + ':' + mm + ':' + ss
};
$.ajax({
url: '/',
type: 'post',
data: postData,
success: function(data){
alert('发布成功!');
location.href = '/';
},
error: function(data){
alert('发布失败!');
location.href = 'error';
}
});
});
});
</script>

node.js接收数据并将数据保存到数据库

mongoose 是一个文档对象模型库(ODM),它的语法和 mongodb 里的 shell 命令是一样的。
如果你使用过 node.js 直接操作 mongodb,你会发现代码中会出现很多嵌套、回调以及各种潜在问题。
但有了 mongoose,你可以直接在 node.js 里使用 mongoose 自身的语法,不仅代码简洁,操作数据方便,而且避免了很多额外的问题
要使用它,就得先安装它

1
npm install mongoose --save

在 data 文件夹下新增 mongoose.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog');
var blogSchema = new mongoose.Schema({
content: {type: String, unique:true}, // unique 保证数据的唯一,但有时候不管用
date: String
}, {collection: 'post'});
var post = mongoose.model('post', blogSchema);
module.exports = post;

然后在根目录下 app.js 里的 var app = express(); 下一行增加以下代码

1
global.post = require('./data/mongoose');

因为前面ajax是将数据 post 到 index.html 页面,因此我们需要在 index.js 处理 post 的请求。在 index.js 里新增代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* POST home page. */
router.post('/', function(req, res) {
var content = req.body.content;
var date = req.body.date;
if (content && date) {
var newPost = new post({
content: content,
date: date
});
newPost.save(function (err) {
if (err) {
console.error(err);
return;
}
// newPost is saved!
console.log('保存成功!');
res.send(200);
});
}
});

重启服务

1
npm start

刷新页面,然后在文本框里输入 ‘夜晚,这是一个很安静的夜晚!’ ,点击 ‘发布’ 按钮后,会弹出 ‘发布成功!’ 的提示。

然后,我们在操作数据库的命名行面板中,输入查找命令,即可打印出我们刚才发布的内容,这也说明数据在数据库里保存成功了。

1
db.post.find()

读取数据库数据并将数据显示至页面

在刚才的操作中,我们只是点击 ‘发布’ 按钮,将数据 post 到博客首页,并将数据保存到数据库。
但是博客首页刷新后,我们并没有写任何读取数据库的代码
因此,需要在博客首页刷新时(也就是 get 时),去读取数据库,并把读取的数据发送给前端模板,再通过模板把这些数据渲染出来
将 index.js 里的

1
2
3
4
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: '博客首页', name: '博客'});
});

修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* GET home page. */
router.get('/', function(req, res, next) {
post.find({}, function(err, docs) {
if (err) {
console.error(err);
return;
}
// docs 是包含了符合条件的多个文档的一个数组
// console.log(docs);
res.render('index', { title: '博客首页', name: '博客', content: docs.reverse()});
});
});

在这里,docs 是读取数据库后返回的数据,它是一个数组,我使用了 docs.reverse() 对数组进行反转,也就是让最新的数据,渲染在博客列表的最前面
然后,我们再回到模板里。因为,后台返回给前台的数据是动态的,因此也需要对 index.html 中的列表部分进行修改,将 index.html 里的

1
2
3
4
5
6
7
8
9
10
11
12
<div class="blog">
<ul class="blog-list">
<li class="blog-item">
<p class="blog-content">你好</p>
<p class="blog-extra">
<span class="date">2015-10-10</span>
<a href="javascript:;" class="delete">删除</a>
<a href="javascript:;" class="update">更新</a>
</p>
</li>
</ul>
</div>

修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="blog">
<ul class="blog-list">
<% for(var i=0; i<content.length; i++) { %>
<li class="blog-item">
<p class="blog-content"><%- content[i].content %></p>
<p class="blog-extra">
<span class="date"><%= content[i].date %></span>
<a href="javascript:;" class="delete" data-content="<%= content[i].content %>">删除</a>
<a href="javascript:;" class="update" data-content="<%= content[i].content %>">更新</a>
</p>
</li>
<% } %>
</ul>
</div>

再次刷新页面,便能看到 ‘夜晚,这是一个很安静的夜晚!’ 这条数据了

数据的删除与更新

index.html 列表里有这样的代码

1
2
<a href="javascript:;" class="delete" data-content="<%= content[i].content %>">删除</a>
<a href="javascript:;" class="update" data-content="<%= content[i].content %>">更新</a>

这里的 content[i].content 就是每条博客的内容。当点击 ‘删除’ 和 ‘更新’ 这个按钮时,
我们会根据它的 data-content 属性来获取这条博客的内容,然后对符合内容的数据进行删除和更新操作

数据的删除

发送博客类似,这里的删除操作也是通过发送 ajax 给后台,在 index.html 里的js部分,新增以下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
// 删除
$('.delete').on('click', function() {
var deleteContent = $(this).attr('data-content');
var postData = {
'deleteContent': deleteContent
};
if (confirm('您确定要删除这条博客吗?')) {
$.ajax({
url: '/',
type: 'post',
data: postData,
success: function(data){
// alert('成功!');
location.href = '/';
},
error: function(data){
// alert('失败!');
location.href = 'error';
}
});
}
});

前台页面发送了 post 提交请求,那后台就需要处理这个 post 请求。
于是,index.js 新增删除数据的代码,即 index.js 中的 router.post 部分更新为

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
/* POST home page. */
router.post('/', function(req, res) {
var content = req.body.content;
var date = req.body.date;
if (content && date) {
var newPost = new post({
content: content,
date: date
});
newPost.save(function (err) {
if (err) {
console.error(err);
return;
}
// newPost is saved!
console.log('保存成功!');
res.send(200);
});
}
var deleteContent = req.body.deleteContent;
if (deleteContent) {
post.remove({content: deleteContent}, function(err) {
if (err) {
console.error(err);
return;
}
console.log('删除成功!');
res.send(200);
});
}
});

删除数据的功能基本完成。我们运行下重启服务的命令

1
npm start

刷新浏览器页面,发布一条内容为 ‘abcd’ 的博客后,然后点 ‘删除’ 按钮,
首先它会提示你 ‘您确定要删除这条博客吗?’ ,点击 ‘确定’ 按钮后,页面刷新, ‘abcd’ 这条博客就在页面里被删除了。
你也可以在数据库命令面板中运行 db.post.find(),查看该条数据是否真的已删除

数据的更新

数据的更新有点特别,这点在文章的开头也能看出。因为博客的更新是在 update.html 完成的,你可以先回到前面看下博客更新页的UI界面
博客更新的流程,主要分为3步
1.在博客首页点击 ‘更新’ 按钮后,将该条博客的内容作为更新页地址的参数,并且此时,页面跳转到博客更新页(update.html)。简单来说,就是把博客内容带到更新页。
2.在博客更新页读取url里的参数,并将它赋值给文本框中。然后在文本框修改博客内容,点击文本框底部右边的 ‘更新’ 按钮,将文本框里的内容通过ajax的方式 post 到博客首页。其实这里的更新操作和博客首页的发布博客类似。
3.post 到博客首页后,页面将会跳转到博客首页,博客首页在后台处理 post 请求,mongoose代码更新该条博客数据,并将新的博客内容显示出来
第一步,在 index.html 中新增js代码

1
2
3
4
5
// 更新跳转
$('.update').on('click', function() {
var updateContent = $(this).attr('data-content');
location.href = '/update?updateBlog=' + updateContent;
});

第二步,在 update.html 中新增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
<script type="text/javascript" src="/javascripts/jquery.min.js"></script>
<script>
$(function() {
// 从url读取要更新的内容,简单粗暴处理
var oldContent = decodeURI(window.location.search.substr(12));
// 设置它到文本域,并保留换行
$('.textarea-add').val(oldContent.replace(/\<br\/>/g, '\n'));
// 更新
$('.btn-update').on('click', function() {
var updateContent = $('.textarea-add').val().trim().replace(/\n/g, '<br/>');
if (updateContent === oldContent) {
alert('内容没有更新!');
return;
}
var postData = {
'oldContent': oldContent,
'updateContent': updateContent
};
$.ajax({
url: '/',
type: 'post',
data: postData,
success: function(data){
alert('更新成功!');
location.href = '/';
},
error: function(data){
alert('更新失败!');
location.href = 'error';
}
});
});
});
</script>

这里需要注意下,url里的参数必须 decodeURI 转码下才能赋值给文本框,并且html标签里的换行(
)与文本框的换行(\n)也是不同的,
在读取操作时也要进行特殊处理。还有,如果博客内容没有发生变化,要给予提示
第三步,将 index.js 中的 router.post 部分更新为:

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
/* POST home page. */
router.post('/', function(req, res) {
var content = req.body.content;
var date = req.body.date;
if (content && date) {
var newPost = new post({
content: content,
date: date
});
newPost.save(function (err) {
if (err) {
console.error(err);
return;
}
// newPost is saved!
console.log('保存成功!');
res.send(200);
});
}
var deleteContent = req.body.deleteContent;
if (deleteContent) {
post.remove({content: deleteContent}, function(err) {
if (err) {
console.error(err);
return;
}
console.log('删除成功!');
res.send(200);
});
}
var oldContent = req.body.oldContent,
updateContent = req.body.updateContent;
if (oldContent && updateContent) {
post.update({content: oldContent}, {$set: {'content': updateContent}}, function(err) {
if (err) {
console.error(err);
return;
}
console.log('更新成功!');
res.send(200);
});
}
});

重启服务的命令,来检测下更新博客的功能

1
npm start

刷新浏览器页面,首先我们发布一条内容为 ‘123’ 的博客后,然后点 ‘更新’ 按钮,页面跳转到博客更新页,
在文本框里将 ‘123’ 更新为 ‘456’,再点 ‘更新’ 按钮后,页面会提示 ‘更新成功!’ ,
并且此时,页面将跳转到博客首页,你也将看到之前 ‘123’ 的博客被更新为 ‘456’。