待办清单项目传入变量后Handlebars模板不渲染且无报错如何排查?
问题现象
我正在开发一个待办清单项目,但是我的Handlebars模板无法渲染。我使用Passport实现谷歌身份认证,新增的待办内容可以正常存入MongoDB数据库。
根据hbs官方文档,如果if块的参数返回false、undefined、null、""、0或[],则该块不会渲染。我已经确认传入了对应的变量,但控制台没有输出任何报错。
我已经被这个问题困扰很久了,以下是我的代码及注释内容:
相关代码
路由代码
const express = require("express"); const router = express.Router(); const todosController = require("../controllers/todos"); router.get("/", todosController.getTodos); router.post("/addTodo", todosController.addTodo); module.exports = router;
控制器代码
const Todo = require("../models/todos"); module.exports = { getTodos: async (req, res) => { // console.log(req.user); // console.log(req.user.googleId); try { const todoItems = await Todo.find({ googleId: req.user.googleId }); // get all todos that match googleId const itemsLeft = await Todo.countDocuments({ googleId: req.user.googleId, done: false, }); // getting variables from google auth here console.log(itemsLeft); res.render("todos.hbs", { todo: todoItems, // Storing variables here and passing them on into hbs. left: itemsLeft, user: req.user, }); } catch (err) { console.error(err); } }, addTodo: async (req, res) => { // console.log(req.body.todoItem); try { await Todo.create({ todo: req.body.todoItem, done: false, googleId: req.user.googleId, }); console.log("To do has been added!"); res.redirect("/"); } catch (err) { console.error(err); } }, };
Handlebars模板代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>To Do</title> </head> <body> <div class="container"> <header class="flexContainer"> <h1 class="title main-font center">Welcome {{user.displayName}}</h1> {{!-- not rendering. Maybe I need to use lean()? --}} </header> <form class="center" action="/todos/addTodo" method="POST"> <input type="text" placeholder="Add a To Do" name="todoItem" /> <button type="submit" class="submitButton"> <i class="fa fa-plus-square"></i> </button> </form> <div class="to-do-list flexContainer"> <ul class="task-list center"> {{#each todo}} {{#if todo.done}} // <span class="complete">{{todo.todo}}</span> {{else}} <h1>unknown</h1> <span class="incomplete">{{todo.todo}}</span> {{/if}} <span class="fa fa-trash delete-todo"></span> {{/each}} </li> </ul> </div> <h4 class="main-font center">Left to do: {{left}}</h4> </div> <script type="text/javascript" src="/js/main.js"></script> </body> </html>
app.js配置代码
const path = require("path"); const express = require("express"); // handle CRUD api // <-- here const exphbs = require("express-handlebars"); // <-- here const mongoose = require("mongoose"); // talk to database const dotenv = require("dotenv"); const morgan = require("morgan"); const passport = require("passport"); // handle all auth const session = require("express-session"); // keep users logged in const MongoStore = require("connect-mongo"); // pass session to database. Can get back in even if server restarts. const connectDB = require("./config/db"); // how we connect to database // Load config // config.env is where all the global variables are stored. dotenv.config({ path: "./config/config.env" }); // Passport config require("./config/passport")(passport); // This call connects to mongodb. connectDB(); // Initialize app const app = express(); // Body parser // this allows us to look into data that was sent from forms app.use(express.urlencoded({ extended: false })); app.use(express.json()); if (process.env.NODE_ENV === "development") { app.use(morgan("dev")); } // Handlebars <-- here app.engine( ".hbs", exphbs({ defaultLayout: "main", extname: ".hbs", }) ); app.set("view engine", ".hbs"); // <-- here // Sessions app.use( session({ secret: "keyboard cat", resave: false, saveUninitialized: false, store: MongoStore.create({ mongoUrl: process.env.MONGO_URI }), }) ); // Passport middleware app.use(passport.initialize()); app.use(passport.session()); // Static folder app.use(express.static(path.join(__dirname, "public"))); const PORT = process.env.PORT || 3000; // Routes app.use("/", require("./routes/index")); app.use("/auth", require("./routes/auth")); app.use("/todos", require("./routes/todos")); app.listen( PORT, console.log(`Server running in ${process.env.NODE_ENV} mode on PORT ${PORT}`) );
项目目录结构

修复方案
一共有三处问题需要修改:
- Mongoose查询返回的是带元数据的文档对象,不是普通JS对象,Handlebars无法直接读取属性。需要在查询时加
lean()方法,将结果转为普通对象。修改控制器中的查询代码:
// 原代码 const todoItems = await Todo.find({ googleId: req.user.googleId }); // 修改后 const todoItems = await Todo.find({ googleId: req.user.googleId }).lean();
如果user.displayName无法渲染,也可以将req.user转为普通对象再传入模板:
res.render("todos.hbs", { todo: todoItems, left: itemsLeft, user: req.user.toJSON() });
- Handlebars的
each循环内,上下文默认指向当前遍历的子项,不需要再加外层的todo.前缀。同时模板中存在多余的</li>闭合标签,缺少对应的<li>开始标签,需要补全结构。修改模板的列表部分:
<div class="to-do-list flexContainer"> <ul class="task-list center"> {{#each todo}} <li> {{#if done}} <span class="complete">{{todo}}</span> {{else}} <h1>unknown</h1> <span class="incomplete">{{todo}}</span> {{/if}} <span class="fa fa-trash delete-todo"></span> </li> {{/each}} </ul> </div>
修改完成后即可正常渲染模板内容。
内容的提问来源于stack exchange,提问作者Julian
相关产品推荐
相关产品推荐

