首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
安装 Node.js
安装与启动 MongoDB
require
exports 和 module.exports
Promise
环境变量
package.json
npm 使用注意事项
初始化一个 Express 项目
路由
模板引擎
Express 浅析
开发环境
准备工作
配置文件
功能与路由设计
页面设计
连接数据库
注册
登出与登录
文章模块
留言模块
当前位置:
首页>>
技术小册>>
Nodejs开发博客应用
小册名称:Nodejs开发博客应用
我们只需要留言的作者 id、留言内容和关联的文章 id 这几个字段,修改 lib/mongo.js,添加如下代码: **lib/mongo.js** ```js exports.Comment = mongolass.model('Comment', { author: { type: Mongolass.Types.ObjectId, required: true }, content: { type: 'string', required: true }, postId: { type: Mongolass.Types.ObjectId, required: true } }) exports.Comment.index({ postId: 1, _id: 1 }).exec()// 通过文章 id 获取该文章下所有留言,按留言创建时间升序 ``` ## 4.10.2 显示留言 在实现留言功能之前,我们先让文章页可以显示留言列表。首先创建留言的模板,新建 views/components/comments.ejs,添加如下代码: **views/components/comments.ejs** ```ejs <div class="ui grid"> <div class="four wide column"></div> <div class="eight wide column"> <div class="ui segment"> <div class="ui minimal comments"> <h3 class="ui dividing header">留言</h3> <% comments.forEach(function (comment) { %> <div class="comment"> <span class="avatar"> <img src="/img/<%= comment.author.avatar %>"> </span> <div class="content"> <a class="author" href="/posts?author=<%= comment.author._id %>"><%= comment.author.name %></a> <div class="metadata"> <span class="date"><%= comment.created_at %></span> </div> <div class="text"><%- comment.content %></div> <% if (user && comment.author._id && user._id.toString() === comment.author._id.toString()) { %> <div class="actions"> <a class="reply" href="/comments/<%= comment._id %>/remove">删除</a> </div> <% } %> </div> </div> <% }) %> <% if (user) { %> <form class="ui reply form" method="post" action="/comments"> <input name="postId" value="<%= post._id %>" hidden> <div class="field"> <textarea name="content"></textarea> </div> <input type="submit" class="ui icon button" value="留言" /> </form> <% } %> </div> </div> </div> </div> ``` > 注意:我们在提交留言表单时带上了文章 id(postId),通过 hidden 隐藏。 在文章页引入留言的模板片段,修改 views/post.ejs 为: **views/post.ejs** ```ejs <%- include('header') %> <%- include('components/post-content') %> <%- include('components/comments') %> <%- include('footer') %> ``` 新建 models/comments.js,存放留言相关的数据库操作,添加如下代码: **models/comments.js** ```js const marked = require('marked') const Comment = require('../lib/mongo').Comment // 将 comment 的 content 从 markdown 转换成 html Comment.plugin('contentToHtml', { afterFind: function (comments) { return comments.map(function (comment) { comment.content = marked(comment.content) return comment }) } }) module.exports = { // 创建一个留言 create: function create (comment) { return Comment.create(comment).exec() }, // 通过留言 id 获取一个留言 getCommentById: function getCommentById (commentId) { return Comment.findOne({ _id: commentId }).exec() }, // 通过留言 id 删除一个留言 delCommentById: function delCommentById (commentId) { return Comment.deleteOne({ _id: commentId }).exec() }, // 通过文章 id 删除该文章下所有留言 delCommentsByPostId: function delCommentsByPostId (postId) { return Comment.deleteMany({ postId: postId }).exec() }, // 通过文章 id 获取该文章下所有留言,按留言创建时间升序 getComments: function getComments (postId) { return Comment .find({ postId: postId }) .populate({ path: 'author', model: 'User' }) .sort({ _id: 1 }) .addCreatedAt() .contentToHtml() .exec() }, // 通过文章 id 获取该文章下留言数 getCommentsCount: function getCommentsCount (postId) { return Comment.count({ postId: postId }).exec() } } ``` > 小提示:我们让留言也支持了 markdown。 > 注意:删除一篇文章成功后也要删除该文章下所有的评论,上面 delCommentsByPostId 就是用来做这件事的。 修改 models/posts.js,在: **models/posts.js** ```js const Post = require('../lib/mongo').Post ``` 下添加如下代码: ```js const CommentModel = require('./comments') // 给 post 添加留言数 commentsCount Post.plugin('addCommentsCount', { afterFind: function (posts) { return Promise.all(posts.map(function (post) { return CommentModel.getCommentsCount(post._id).then(function (commentsCount) { post.commentsCount = commentsCount return post }) })) }, afterFindOne: function (post) { if (post) { return CommentModel.getCommentsCount(post._id).then(function (count) { post.commentsCount = count return post }) } return post } }) ``` 在 PostModel 上注册了 `addCommentsCount` 用来给每篇文章添加留言数 `commentsCount`,在 `getPostById` 和 `getPosts` 方法里的: ``` .addCreatedAt() ``` 下添加: ``` .addCommentsCount() ``` 这样主页和文章页的文章就可以正常显示留言数了。 然后将 `delPostById` 修改为: ```js // 通过用户 id 和文章 id 删除一篇文章 delPostById: function delPostById (postId, author) { return Post.deleteOne({ author: author, _id: postId }) .exec() .then(function (res) { // 文章删除后,再删除该文章下的所有留言 if (res.result.ok && res.result.n > 0) { return CommentModel.delCommentsByPostId(postId) } }) } ``` > 小提示:虽然目前看起来使用 Mongolass 自定义插件并不能节省代码,反而使代码变多了。Mongolass 插件真正的优势在于:在项目非常庞大时,可通过自定义的插件随意组合(及顺序)实现不同的输出,如上面的 `getPostById` 需要将取出 markdown 转换成 html,则使用 `.contentToHtml()`,否则像 `getRawPostById` 则不必使用。 修改 routes/posts.js,在: **routes/posts.js** ```js const PostModel = require('../models/posts') ``` 下引入 CommentModel: ```js const CommentModel = require('../models/comments') ``` 在文章页传入留言列表,将: ```js // GET /posts/:postId 单独一篇的文章页 router.get('/:postId', function (req, res, next) { ... }) ``` 修改为: ```js // GET /posts/:postId 单独一篇的文章页 router.get('/:postId', function (req, res, next) { const postId = req.params.postId Promise.all([ PostModel.getPostById(postId), // 获取文章信息 CommentModel.getComments(postId), // 获取该文章所有留言 PostModel.incPv(postId)// pv 加 1 ]) .then(function (result) { const post = result[0] const comments = result[1] if (!post) { throw new Error('该文章不存在') } res.render('post', { post: post, comments: comments }) }) .catch(next) }) ``` 现在刷新文章页试试吧,此时已经显示了留言的输入框。 ## 4.10.3 发表与删除留言 现在我们来实现发表与删除留言的功能。将 routes/comments.js 修改如下: ```js const express = require('express') const router = express.Router() const checkLogin = require('../middlewares/check').checkLogin const CommentModel = require('../models/comments') // POST /comments 创建一条留言 router.post('/', checkLogin, function (req, res, next) { const author = req.session.user._id const postId = req.fields.postId const content = req.fields.content // 校验参数 try { if (!content.length) { throw new Error('请填写留言内容') } } catch (e) { req.flash('error', e.message) return res.redirect('back') } const comment = { author: author, postId: postId, content: content } CommentModel.create(comment) .then(function () { req.flash('success', '留言成功') // 留言成功后跳转到上一页 res.redirect('back') }) .catch(next) }) // GET /comments/:commentId/remove 删除一条留言 router.get('/:commentId/remove', checkLogin, function (req, res, next) { const commentId = req.params.commentId const author = req.session.user._id CommentModel.getCommentById(commentId) .then(function (comment) { if (!comment) { throw new Error('留言不存在') } if (comment.author.toString() !== author.toString()) { throw new Error('没有权限删除留言') } CommentModel.delCommentById(commentId) .then(function () { req.flash('success', '删除留言成功') // 删除成功后跳转到上一页 res.redirect('back') }) .catch(next) }) }) module.exports = router ``` 至此,我们完成了创建留言和删除留言的逻辑。刷新页面,尝试留言试试吧。留言成功后,将鼠标悬浮在留言上可以显示出 `删除` 的按钮,点击可以删除留言。
上一篇:
文章模块
该分类下的相关小册推荐:
Nodejs底层原理与源码解读