Todo案例
1.为todo数据库添加账号
- 使用mongo命令进入mongodb数据库
- 使用use admin命令进入到admin数据中
- 使用db.auth(‘root’, ‘root’)命令登录数据库
- 使用use todo命令切换到todo数据库
- 使用db.createUser({user: ‘cwen’, pwd: ‘jdoit’, roles: [‘readWrite’]})创建todo数据库账号
- 使用exit命令退出mongodo数据库
附上我的实操图:
注意:若数据库没有账号,此处可省略(关于加密方式,点击:可看我的另一篇博文)
2.展示任务列表
- 准备一个放置任务列表的数组
- 向服务器端发送请求,获取已存在的任务
- 将已存在的任务存储在任务列表数组中
- 通过模板引擎将任务列表数组中的任务显示在页面中
注意:数组的操作方便于其大量的方法,此次数组不多余。
3.添加任务
- 为文本框绑定键盘抬起事件,在事件处理函数中判断当前用户敲击的是否是回车键
- 当用户敲击回车键的时候,判断用户在文本框中是否输入了任务名称
- 向服务器端发送请求,将用户输入的任务名称添加到数据库中,同时将任务添加到任务数组中
- 通过模板引擎将任务列表数组中的任务显示在页面中
4.删除任务
- 为删除按钮添加点击事件
- 在事件处理函数中获取到要删任务的id
- 向服务器端发送请求,根据ID删除任务,同时将任务数组中的相同任务删除
- 通过模板引擎将任务列表数组中的任务重新显示在页面中
5.更改任务状态
- 为任务复选框添加onchange事件
- 在事件处理函数中获取复选框是否选中
- 向服务器端发送请求,将当前复选框的是否选中状态提交到服务器端
- 将任务状态同时也更新到任务列表数组中
- 通过模板引擎将任务列表数组中的任务重新显示在页面中并且根据任务是否完成为li元素添加completed类名
6.修改任务名称
- 为任务名称外层的label标签添加双击事件,同时为当前任务外层的li标签添加editing类名,开启编辑状态
- 将任务名称显示在文本框中并让文本框获取焦点
- 当文本框离开焦点时,将用户在文本框中输入值提交到服务器端,并且将最新的任务名称更新到任务列表数组中
- 使用模板引擎重新渲染页面中的任务列表。
7.计算未完成任务数量
- 准备一个用于存储未完成任务数量的变量
- 将未完成任务从任务数组中过滤出来
- 将过滤结果数组的长度赋值给任务数量变量
- 将结果更新到页面中
8.显示未完成任务
- 为active按钮添加点击事件
- 从任务列表数组中将未完成任务过滤出来
- 使用模板引擎将过滤结果显示在页面中
9.清除已完成任务
- 为clear completed按钮添加点击事件
- 向服务器端发送请求将数据库中的已完成任务删除掉
- 将任务列表中的已完成任务删除调用
- 使用模板引擎将任务列表中的最后结果显示在页面中
任务清晰,开始干活:
服务器端此处模块化思想,对路由与服务器入口进行适当地分离
- app.js(开启服务器文件)
// 引入express框架
const express = require('express');
//引入path模块
const path = require('path');
//引入mongoose模块
const mongoose = require('mongoose');
//引入接收post参数的bady-parser模块
const bodyParser = require('body-parser');
//搭建服务器
const app = express();
//实现静态页面访问功能
app.use(express.static(path.join(__dirname, 'public')));
//实现post请求参数的解析
// app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//cwen 连接数据库
mongoose.connect('mongodb://cwen:cwen@localhost:27017/todo', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('数据库连接成功'))
.catch(err => console.log(err, '数据库连接失败'))
// 导入二级路由
app.use('/todo', require('./router/todo'));
//监听端口
app.listen(3000);
console.log('服务器启动成功,端口3000');
- task.js(任务集合)
// 引入mongoose模块
const mongoose = require('mongoose');
// 创建任务集合规则
const taskSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
completed: {
type: Boolean,
default: false
}
}, { versionKey: false });
// 创建todo集合
const Task = mongoose.model('task', taskSchema);
// 将集合构造函数作为模块成员进行导出
module.exports = Task;
- todo.js(路由模块)
// 引入express框架
const express = require('express');
// 引入Joi验证模块
const Joi = require('joi');
// 工具库引入
const _ = require('lodash');
//搭建二级路由
const todoRouter = express.Router();
//导入集合模块
const Task = require('../model/task');
//!实现页面展示功能
todoRouter.get('/task', async(req, res) => {
const task = await Task.find();
res.send(task);
});
//!实现添加功能
// 添加任务
todoRouter.post('/addTask', async(req, res) => {
// 接收客户端传递过来的任务名称
const { title } = req.body;
// 验证规则
const schema = {
title: Joi.string().required().min(2).max(30)
};
// 验证客户端传递过来的请求参数
const { error } = Joi.validate(req.body, schema);
// 验证失败
if (error) {
// 将错误信息响应给客户端
return res.status(400).send({ message: error.details[0].message })
}
// 创建任务实例
const task = new Task({ title: title, completed: false });
// 执行插入操作
await task.save();
// 响应
setTimeout(() => {
res.send(task);
}, 2000)
});
//!实现删除功能
todoRouter.get('/deleteTask', async(req, res) => {
// 要删除的任务id
const { _id } = req.query;
// 验证规则
const schema = {
_id: Joi.string().required().regex(/^[0-9a-fA-F]{24}$/)
};
// 验证客户端传递过来的请求参数
const { error } = Joi.validate(req.query, schema);
// 验证失败
if (error) {
// 将错误信息响应给客户端
return res.status(400).send({ message: error.details[0].message })
}
// 删除任务
const task = await Task.findOneAndDelete({ _id: _id });
// 响应
res.send(task);
});
//! 修改任务
todoRouter.post('/modifyTask', async(req, res) => {
// 执行修改操作
const task = await Task.findOneAndUpdate({ _id: req.body._id }, _.pick(req.body, ['title', 'completed']), { new: true })
// 响应
res.send(task);
});
//! 清除已完成任务
todoRouter.get('/clearTask', async(req, res) => {
// 执行清空操作
await Task.deleteMany({ completed: true });
//再次执行查询操作,查出剩余没有完成的任务
const result = await Task.find();
// 返回清空数据
res.send(result);
});
//模块导出
module.exports = todoRouter;
- index.html(静态页面)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Todo List</title>
<link rel="stylesheet" href="assets/css/base.css">
<link rel="stylesheet" href="assets/css/index.css">
<!-- <style>
.toggle {
background-color: rebeccapurple!important;
}
</style> -->
</head>
<body>
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus id="taskInp">
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main">
<input class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<!--cwen 任务列表 -->
<ul class="todo-list" id="todoBox"></ul>
</section>
<!-- This footer should hidden by default and shown when there are todos -->
<footer class="footer">
<!-- This should be `0 items left` by default -->
<span class="todo-count"><strong>0</strong> item left</span>
<!-- Remove this if you don't implement routing -->
<ul class="filters">
<li>
<a class="selected" href="#">All</a>
</li>
<li>
<a class="undone" href="#">Active</a>
</li>
<li>
<a class="completed" href="#">Completed</a>
</li>
</ul>
<!-- Hidden if no completed items are left ↓ -->
<button class="clear-completed">Clear completed</button>
</footer>
</section>
<script src="../js/jquery.min.js"></script>
<script src="../js/template-web.js"></script>
<!--cwen 创建任务模板 -->
<script type="text/html" id="taskTpl">
{{each task}}
<li class="{{$value.completed ? 'completed' : ''}}">
<div class="view">
<input class="toggle" type="checkbox" {{$value.completed ? 'checked' : ''}}>
<label>{{$value.title}}</label>
<button class="destroy" data-id="{{$value._id}}"></button>
</div>
<input class="edit" value="Rule the web">
</li>
{{/each}}
</script>
<script>
//用于存储任务的数组
var taskAry = [];
// 获取容器
var todoBox = $('#todoBox');
var taskInp = $('#taskInp');
var strong = $('strong');
// 发送ajax请求(获取已有的任务)
$.ajax({
type: 'get',
url: '/todo/task',
success: function(response) {
// 将已存在的任务存储在taskAry数组中
taskAry = response;
// 调用模板拼接函数
render();
//计算完成任务数
completedCount();
}
})
// 添加功能(添加任务)
// 获取文本框并且添加键盘抬起事件
taskInp.on('keyup', function(event) {
// 如果用户敲击的是回车键
if (event.keyCode == 13) {
// 判断用户是否在文本框中输入了任务名称
var taskName = $(this).val();
// 如果用户没有在文本框中输入内容
if (taskName.trim().length == 0) {
alert('请输入任务名称')
// 阻止代码向下执行
return;
}
// 向服务器端发送请求 添加任务
$.ajax({
type: 'post',
url: '/todo/addTask',
contentType: 'application/json',
data: JSON.stringify({
title: taskName
}),
success: function(response) {
// 将任务添加到任务列表中
taskAry.push(response);
// 拼接字符串 将拼接好的字符串显示在页面中
render();
//计算未完成任务数
completedCount();
// 清空文本框中的内容
taskInp.val('');
}
})
}
});
// 删除功能
// 事件委托,选中删除按钮
todoBox.on('click', '.destroy', function() {
//获取id
var id = $(this).attr('data-id');
//向网页发送删除任务
$.ajax({
url: '/todo/deleteTask',
type: 'get',
data: {
_id: id
},
success: function(response) {
// 从任务数组中找到要删除的任务索引
var index = taskAry.findIndex(item => item._id == id);
//将任务从数组中删除
taskAry.splice(index, 1);
//重新渲染
render();
//计算未完成任务数
completedCount();
}
})
});
//当用户改变任务名称前面的复选框状态时触发
todoBox.on('change', '.toggle', function() {
// 获取复选框状态
const status = $(this).is(':checked');
// 通过兄的节点得到id
const id = $(this).siblings('button').attr('data-id');
// 发送ajax请求
$.ajax({
type: 'post',
url: '/todo/modifyTask',
data: JSON.stringify({
_id: id,
completed: status
}),
contentType: 'application/json',
success: function(response) {
//查找那个任务
var task = taskAry.find(item => item._id == id);
// 把那个任务的completed修改
task.completed = response.completed;
// 重新渲染
render();
//计算未完成任务数
completedCount();
}
});
});
// 修改任务名称
todoBox.on('dblclick', 'label', function() {
//让label变为可编辑的文本框
$(this).parent().parent().addClass('editing');
//让文本显示在编辑框,且焦点聚集
$(this).parent().siblings('input').focus().val($(this).text());
});
//失去焦点进行修改
todoBox.on('blur', '.edit', function() {
// 获取任务id和任务名称
var id = $(this).siblings().find('button').attr('data-id');
var newTask = $(this).val();
// 把修改传递给服务器
$.ajax({
type: 'post',
url: '/todo/modifyTask',
data: JSON.stringify({
_id: id,
title: newTask
}),
contentType: 'application/json',
success: function(response) {
//变为label标签
// $(this).removeClass('editing');
//将服务器返回值加入数组
var task = taskAry.find(item => item._id == id);
task.title = response.title;
// 重新渲染
render();
//计算未完成任务数
completedCount();
}
});
});
// 显示所有任务
$('.selected').on('click', function() {
render();
});
//显示未完成任务
$('.undone').click(function() {
//对数组进行过滤
const unCompleted = taskAry.filter(item => item.completed == false);
// console.log(unCompleted);
//把新数组给任务列表
var html = template('taskTpl', {
task: unCompleted
});
//填入容器
todoBox.html(html);
});
//显示完成任务
$('.completed').click(function() {
//对数组进行过滤
const Completed = taskAry.filter(item => item.completed == true);
// console.log(unCompleted);
//把新数组给任务列表
var html = template('taskTpl', {
task: Completed
});
//填入容器
todoBox.html(html);
});
//清除已完成任务
$('.clear-completed').click(function() {
$.ajax({
type: 'get',
url: '/todo/clearTask',
success: function(response) {
taskAry = response;
render();
}
});
});
// 封装字符串拼接,模板展示的函数
function render() {
//模板拼接
const html = template('taskTpl', {
task: taskAry
});
//填入容器
todoBox.html(html);
}
//封装计算未完成数量
function completedCount() {
// 存储结果的变量
var count = 0;
// 将未完成的任务过滤到一个新的数组中
var newAry = taskAry.filter(item => item.completed == false);
// 将新数组的长度赋值给count
count = newAry.length;
// 将未完成的任务数量显示在页面中
strong.text(count)
}
</script>
</body>
</html>
emmm,加油,铁汁
下一篇:RESTful 和 XML