这是一个用Node完成的待办事项的Demo,支持手机端和PC浏览器端同时查看。下载地址:https://github.com/yangfanacc/Todo 在线查看效果可以访问这个网址:http://123.56.44.245:3460 效果图如下: 首先介绍一个这个待办事项示例项目的搭建环境:
1.Nodejs版本:v0.10.35
2.Mongodb(使用Mongoose连接Mongodb数据库)
3.前台使用了国内比较好用的开源框架[Amaze](http://amazeui.org/)
项目的结构二级截图如下: 项目思路 首先,需要一个网址可以用来访问我们的待办事项首页(对数据库的读操作)。然后我们需要有添加和删除待办事项的对数据库的增加和删除(更新)操作。其实,也就这么多了,很简单的是不是?不过这里我们可不是真的删除,因为我们有可能需要保存我们的待办事项历史,大家可以访问这个网址:http://123.56.44.245:3460/all 是的,我们在删除待办事项的时候,是需要一个bool参数在数据库中,只是把数据变为false不在读取,而不是真的删除。这和我们使用腾讯的QQ空间是一样的效果嘛,哈哈:)
第一步 我们需要确定使用那些类库,我们这里使用的类库如下:
1.body-parser
2.cookie-parser
3.debug
4.ejs
5.express
6.mongoose
7.morgan
8.serve-favicon
大家也看到了,除了第6个是我自己加的以外,其他的都是我们在使用express -e TodoList这个express模板的使用express所需要的。我们我们只是需要添加这个mongoose模块就好了cnpm install mongoose --save。如果上面的添加mongoose模块中的cnpm和–save你还不知道是什么意思,建议你去google一下喽.
第二步 从上面我们可以看到,我们使用的是ejs模板。但是在使用ejs的时候,我很不习惯加上ejs这个后缀,我知道不止我一个人有这样的爱好:)。所以这里我们开始将ejs转换为html的后缀使用。方法大家可以去Google一下,这里贴出了:
var ejs = require('ejs');
app.engine('html', ejs.__express);
app.set('view engine', 'html');
修改app.js文件中的相关代码就可以了:) 然后,就是确定我们需要用到几个html页面了。其实就是上面的网址我们看到的,一个是主页面,还有一个是历史查看页面。这两个页面的差别真的不大啦,其实是可以用一个页面来完成的,但是这里我用的两个页面用一个页面会更方便,我这里当时没有想到。 然后就是我们需要一个前端的框架,那就是前文提到的Amaze,还不会使用的小伙伴们可以去官网使用一下,很简单的,用一下就会了。 上面的assets文件夹里面的就是Amaze所必须的相关文件,我们放到public文件夹就好了,这个静态文件目录我们是可以直接读取到的。 之后我们就可以在html页面使用前端的框架了。 index.html文件代码如下:
<!doctype html>
<html class=“no-js”>
<head>
<meta charset=“utf-8”>
<meta http-equiv=“X-UA-Compatible” content=“IE=edge”>
<meta name=“description” content="">
<meta name=“keywords” content="">
<meta name="viewport"
content=“width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”>
<title>随时记事</title>
<!-- Set render engine for 360 browser --> <meta name=“renderer” content=“webkit”>
<!-- No Baidu Siteapp–> <meta http-equiv=“Cache-Control” content=“no-siteapp”/>
<link rel=“icon” type=“image/png” href=“assets/i/favicon.png”>
<!-- Add to homescreen for Chrome on Android --> <meta name=“mobile-web-app-capable” content=“yes”> <link rel=“icon” sizes=“192x192” href=“assets/i/app-icon72x72@2x.png”>
<!-- Add to homescreen for Safari on iOS --> <meta name=“apple-mobile-web-app-capable” content=“yes”> <meta name=“apple-mobile-web-app-status-bar-style” content=“black”> <meta name=“apple-mobile-web-app-title” content=“Amaze UI”/> <link rel=“apple-touch-icon-precomposed” href=“assets/i/app-icon72x72@2x.png”>
<!-- Tile icon for Win8 (144x144 + tile color) --> <meta name=“msapplication-TileImage” content=“assets/i/app-icon72x72@2x.png”> <meta name=“msapplication-TileColor” content="#0e90d2">
<link rel=“stylesheet” href=“assets/css/amazeui.min.css”>
<link rel=“stylesheet” href=“assets/css/app.css”>
</head>
<body>
<header data-am-widget=“header” class=“am-header am-header-default”>
<h1 class=“am-header-title”>
<a href="#title-link" class="">For Adron</a>
</h1>
</header>
<!–在这里编写你的代码–>
<ul id=“contentUl” class=“am-list am-list-static am-list-border am-list-striped”>
<% allContent.forEach(function(todo, index){ %>
<li ><%= todo.content %> <button onclick=“deleteContent(’<%= todo.id %>’, this)” style=“float:right;” type=“button” class=“am-close”>×</button></li>
<% }) %>
</ul>
<div class=“am-u-lg-6”>
<div class=“am-input-group”>
<input id=“content” type=“text” class=“am-form-field”>
<span class=“am-input-group-btn”>
<button class=“am-btn am-btn-default” type=“button” onclick=“addContent()”>添加</button>
</span>
</div>
</div>
<script>
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject(“Microsoft.XMLHTTP”);
}
function addContent() {
// var content = document.getElementById(“content”).value;
var content = $("#content").val();
xmlhttp.open(“GET”,"/add?content="+content,false);
xmlhttp.send();
if (xmlhttp.status == 200) {
var contentUl=document.getElementById(‘contentUl’);
var li = document.createElement(“li”);
var id = “’” + xmlhttp.responseText + “’”;
var onclickStr = ‘<button onclick="deleteContent(’ + id + ', this)" style=“float:right;” type=“button” class=“am-close”>×</button>'
li.innerHTML = content + onclickStr;
contentUl.insertBefore(li,contentUl.childNodes[0]);
$("#content").val("");
}
}
function deleteContent(id, obj) {
$(obj).parent().remove();
xmlhttp.open("GET","/delete?id="+id,true);
xmlhttp.send();
if (xmlhttp.status == 200) {
}
}
</script>
<!–[if (gte IE 9)|!(IE)]><!–>
<script src=“assets/js/jquery.min.js”></script>
<script src=“assets/js/amazeui.min.js”></script>
<!–<![endif]–>
<!–[if lte IE 8 ]>
<script src=“http://libs.baidu.com/jquery/1.11.1/jquery.min.js”></script>
<![endif]–>
</body>
</html>
大家可以先看开头和结尾,使用了css和js的部分,其他的后面会说:)
对数据库的增删读改操作 对数据库的操作文件如下图: 我们mkdir models文件夹 首先,我们建立一个对数据库的文件配置操作:mongodb.js
var mongoose = require(‘mongoose’);
mongoose.connect(‘mongodb://127.0.0.1/MorePersonTodo’);
exports.mongoose = mongoose;
这里我们配置了数据库的名称MorePersonTodo。当然,对如何使用mongoose还不熟悉的朋友们,建议Google一下相关的使用方法。
Todo.js文件就是具体的操作了,代码如下:
[yangfan-23:37-~/TodoForMorePerson/models-49]cat Todo.js
var mongodb = require(’./mongodb.js’);
var Schema = mongodb.mongoose.Schema;
var TodoSchema = new Schema({
content: String,
show: Boolean
});
var TodoModel = mongodb.mongoose.model(“Todo”, TodoSchema);
function Todo(content, show) {
this.content = content;
this.show = show;
};
Todo.prototype.save = function(todo, callback) {
var todo = {
content: todo.content,
show: todo.show
};
var newTodo = new TodoModel(todo);
newTodo.save(function (err, todo) { if (err) { return callback(err); } callback(null, todo); }); };
Todo.prototype.get = function(callback) {
TodoModel.find({‘show’:true}, function (err, todos) {
if (err) {
return callback(err);
}
callback(null, todos);
});
};
Todo.prototype.getAll = function(callback) {
TodoModel.find(function (err, todos) {
if (err) {
return callback(err);
}
callback(null, todos);
});
};
Todo.prototype.delete = function(id, callback) {
TodoModel.update({’_id’:id}, {‘show’:false}, function(err) {
if (err) {
return callback(err);
}
callback(null);
});
};
module.exports = Todo;
详细说明如下: 首先我们建立了一个Schema和Model
var mongodb = require(’./mongodb.js’);
var Schema = mongodb.mongoose.Schema;
var TodoSchema = new Schema({
content: String,
show: Boolean
});
var TodoModel = mongodb.mongoose.model(“Todo”, TodoSchema);
function Todo(content, show) {
this.content = content;
this.show = show;
};
然后是写入操作:
Todo.prototype.save = function(todo, callback) {
var todo = {
content: todo.content,
show: todo.show
};
var newTodo = new TodoModel(todo);
newTodo.save(function (err, todo) { if (err) { return callback(err); } callback(null, todo); }); }; 然后是查询操作:
Todo.prototype.get = function(callback) {
TodoModel.find({‘show’:true}, function (err, todos) {
if (err) {
return callback(err);
}
callback(null, todos);
});
};
注意这里我们添加了查询条件是{‘show’:true}我们查询的是标志位true的记录。
然后是得到所有操作,这里我们可以看到历史记录:
Todo.prototype.getAll = function(callback) {
TodoModel.find(function (err, todos) {
if (err) {
return callback(err);
}
callback(null, todos);
});
};
然后就是我们的删除记录操作,当然我们是修改show的参数,而不是真的删除:
Todo.prototype.delete = function(id, callback) {
TodoModel.update({’_id’:id}, {‘show’:false}, function(err) {
if (err) {
return callback(err);
}
callback(null);
});
};
最后,我们把整个Todo导出供其他js文件调用:
module.exports = Todo;
这样,我们的对数据库的操作就完成了:)
路由转发 下面,我们就开始对我们在不同操作下,路由的转发以对数据库进行不同条件下的操作了。 我们的代码在routes/index.js文件中:
[yangfan-23:46-~/TodoForMorePerson/routes-55]cat index.js
var express = require(‘express’);
var Todo = require("…/models/Todo.js");
var router = express.Router();
/* GET home page. */
router.get(’/’, function(req, res) {
var todo = new Todo();
todo.get(function(err, todoBack){
res.render(‘index’, { allContent: todoBack.reverse() });
});
});
router.get(’/add’, function(req, res) {
var content = req.query.content;
var todo = new Todo(content, true);
todo.save(todo, function(err, todoBack){
if (err) {
res.writeHead(500);
} else {
res.writeHead(200);
}
res.write(todoBack.id);
res.end();
});
});
router.get(’/delete’, function(req, res) {
var id = req.query.id;
var todo = new Todo();
todo.delete(id, function(err){
if (err) {
res.writeHead(500);
} else {
res.writeHead(200);
}
res.end();
});
});
router.get(’/all’, function(req, res) {
var todo = new Todo();
todo.getAll(function(err, todoBack){
res.render(‘all’, { allContent: todoBack.reverse() });
});
});
module.exports = router;
这里我就不详细说明了,我想大家看到代码的意思就是懂得了:)
Ajax的使用 我们知道,Ajax是划时代的产物,通过javascript实现这个不刷新网页就可以更新页面的技术真的很好。 1.获取到我们需要的待办事项到我们的主页面中: index.js:
/* GET home page. */
router.get(’/’, function(req, res) {
var todo = new Todo();
todo.get(function(err, todoBack){
res.render(‘index’, { allContent: todoBack.reverse() });
});
});
index.html:
<ul id=“contentUl” class=“am-list am-list-static am-list-border am-list-striped”>
<% allContent.forEach(function(todo, index){ %>
<li ><%= todo.content %> <button onclick=“deleteContent(’<%= todo.id %>’, this)” style=“float:right;” type=“button” class=“am-close”>×</button></li>
<% }) %>
</ul>
这样我们就将所有的结果输出到页面了。大家可能注意到<button>xxx</button>这个位置的代码了,我们下面会说。
2.增加我们的待办事项
index.js:
router.get(’/add’, function(req, res) {
var content = req.query.content;
var todo = new Todo(content, true);
todo.save(todo, function(err, todoBack){
if (err) {
res.writeHead(500);
} else {
res.writeHead(200);
}
res.write(todoBack.id);
res.end();
});
});
index.html:
<script>
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject(“Microsoft.XMLHTTP”);
}
function addContent() {
// var content = document.getElementById(“content”).value;
var content = $("#content").val();
xmlhttp.open(“GET”,"/add?content="+content,false);
xmlhttp.send();
if (xmlhttp.status == 200) {
var contentUl=document.getElementById(‘contentUl’);
var li = document.createElement(“li”);
var id = “’” + xmlhttp.responseText + “’”;
var onclickStr = ‘<button onclick="deleteContent(’ + id + ', this)" style=“float:right;” type=“button” class=“am-close”>×</button>'
li.innerHTML = content + onclickStr;
contentUl.insertBefore(li,contentUl.childNodes[0]);
$("#content").val("");
}
}
function deleteContent(id, obj) {
$(obj).parent().remove();
xmlhttp.open("GET","/delete?id="+id,true);
xmlhttp.send();
if (xmlhttp.status == 200) {
}
}
</script>
我们使用了Ajax。当我们点击“添加”按钮的使用,就触发了javascript中“addContent”函数。这里不对如何使用Ajax进行介绍,如果您还不会,还是Google的说。这里需要注意的是,如果我们添加成功的话,会返回200,这时我们已经获取到添加事项在数据库中的唯一id。我们把这个id放在button中,是为了在删除这个事项的时候唯一确定该事项使用的。
然后,我们通过javascript代码:
var contentUl=document.getElementById(‘contentUl’);
var li = document.createElement(“li”);
var id = “’” + xmlhttp.responseText + “’”;
var onclickStr = ‘<button onclick="deleteContent(’ + id + ', this)" style=“float:right;” type=“button” class=“am-close”>×</button>'
li.innerHTML = content + onclickStr;
contentUl.insertBefore(li,contentUl.childNodes[0]);
把这条记录放置在li列表的第一个位置:)
3.删除一条事项记录
index.js:
router.get(’/delete’, function(req, res) {
var id = req.query.id;
var todo = new Todo();
todo.delete(id, function(err){
if (err) {
res.writeHead(500);
} else {
res.writeHead(200);
}
res.end();
});
});
index.html:
function deleteContent(id, obj) {
$(obj).parent().remove();
xmlhttp.open("GET","/delete?id="+id,true);
xmlhttp.send();
if (xmlhttp.status == 200) {
}
}
我们还是使用Ajax,我们为了速度,是先把这条已经已经li删除了$(obj).parent().remove();:),然后开始执行数据库操作。注意我们删除只是把show参数变为false以不在读取,不是真的删除。 4.读取所有历史记录: index.js
router.get(’/all’, function(req, res) {
var todo = new Todo();
todo.getAll(function(err, todoBack){
res.render(‘all’, { allContent: todoBack.reverse() });
});
});
我们访问xxxx/all就可以看到所有的历史记录了:) 代码下载地址:https://github.com/yangfanacc/Todo 这里谢谢大家给个星星(Star)哦 :)
这里代码排版还是比较乱,是我黏贴的问题,大家可以看这个:http://www.livyfeel.com/nodejsfortodo/或者:http://blog.csdn.net/yangfanacc/article/details/43386465