Handlebars遍历Mongoose对象数组时字符串属性为undefined如何解决
Handlebars渲染Mongoose嵌套数组属性输出undefined解决方案
问题复现代码
当前编写的Handlebars模板代码如下:
{{#if user}} <p>Welcome, <a href="/profile/{{user.[_id]}}">{{user.username}}</a>.</p> {{#each user.snippets}} {{log 'heading' this.heading}} <h1>{{heading}}</h1> <code>{{code}}</code> {{/each}} {{/if}}
已知表现
user.username字段可正常输出user.snippets循环执行次数符合预期,可通过日志确认遍历逻辑正常- 数据库中snippets元素的
heading、code字段已正确存储,但模板渲染结果为undefined - 已尝试调整
this写法、修改属性访问格式、自定义helper,均未解决问题 - 数据来源:
user为Mongoose查询返回的文档实例,snippets是user模型下定义的对象数组属性
根本原因
Mongoose默认查询返回的是Document类实例,不是原生JavaScript普通对象,其属性通过内部getter/setter代理实现。Handlebars在遍历嵌套的子文档数组时,上下文绑定到Mongoose子文档实例后,无法正确穿透内部代理访问到实际存储的属性值,就会返回undefined。
根级user.username可正常访问,是因为根文档的属性访问逻辑被Handlebars兼容,但嵌套数组内的子文档实例没有做对应适配。
可行解决方案
按优先级从高到低排列:
- 方案1(推荐,性能最优):查询数据时链式调用
.lean()方法,直接返回普通JS对象
在原有Mongoose查询逻辑结尾加上.lean()即可:
该方法会跳过Mongoose文档实例化流程,返回的所有数据都是纯JS对象,不存在属性访问兼容问题,同时查询速度更快、内存占用更低。// 原有查询示例(根据实际业务的查询逻辑调整) // const user = await User.findById(req.params.userId) // 修改为 const user = await User.findById(req.params.userId).lean() - 方案2:查询完成后手动调用
.toObject()转普通对象再传入模板
如果业务逻辑需要用到Mongoose文档实例的内置方法(比如保存实例、调用实例方法),可以在传给模板前单独转一次普通对象:const userDoc = await User.findById(req.params.userId) // 此处可正常调用userDoc的Mongoose实例方法 const userForRender = userDoc.toObject() res.render('userPage', { user: userForRender }) - 方案3(临时兼容,不推荐):在each块内所有属性前显式加
this.前缀
部分版本组合下,显式指定上下文为当前遍历项可以触发Mongoose的属性getter:
该方案兼容性不稳定,版本变动后可能再次失效,优先选择前两种转普通对象的方案。{{#each user.snippets}} <h1>{{this.heading}}</h1> <code>{{this.code}}</code> {{/each}}
内容的提问来源于stack exchange,提问作者Dominus
相关产品推荐
相关产品推荐

