node.js - ../this在父级和子级具有相同值时在内部循环内返回 View 对象

标签 node.js express handlebars.js view-helpers

我只是从handlebars开始,我正在尝试做一个简单的double for循环,以使整天的时间间隔为15分钟。如果子代和父代具有相同的值,则会返回view对象,这是一件很奇怪的事情。这是我的代码:

var handlebarsExpress = require('express-handlebars').create({
    helpers: {
        for: function(from, to, incr, block) {
            var accum = '';
            for(var i = from; i <= to; i += incr) {
                accum += block.fn(i);
            }
            return accum;
        }
    }
});

app.engine('handlebars', handlebarsExpress.engine);
app.set('view engine', 'handlebars');

...
...


在查看文件(sth.handlebars)中,我具有以下内容:

<div class="row columns large-12">
    {{#for 0 23 1}}
        {{#for 0 45 15}}
            {{log ../this}}
            <span>{{../this}}:{{this}}</span><br>
        {{/for}}
    {{/for}}
</div>


html输出是这样的:

[object Object]:0
0:15
0:30
0:45
1:0
...
...
13:45
14:0
14:15
14:30
14:45
15:0
[object Object]:15
15:30
15:45
16:0
16:15


log帮助器在终端中报告以下内容:

{ settings: 
   { 'x-powered-by': true,
     etag: 'weak',
     'etag fn': [Function: wetag],
     env: 'development',
     'query parser': 'extended',
     'query parser fn': [Function: parseExtendedQueryString],
     'subdomain offset': 2,
     'trust proxy': false,
     'trust proxy fn': [Function: trustNone],
     view: [Function: View],
     views: '/home/elsa/Projects/rental-borg/project/views',
     'jsonp callback name': 'callback',
     'view engine': 'handlebars',
     port: 8765 },
  flash: { info: undefined, error: undefined },
  _locals: { flash: { info: undefined, error: undefined } },
  cache: false }
0
0
0
1
1
1
1
2
2
2
2
3
3
3
3
4
...
...
13
13
13
13
14
14
14
14
15
{ settings: 
   { 'x-powered-by': true,
     etag: 'weak',
     'etag fn': [Function: wetag],
     env: 'development',
     'query parser': 'extended',
     'query parser fn': [Function: parseExtendedQueryString],
     'subdomain offset': 2,
     'trust proxy': false,
     'trust proxy fn': [Function: trustNone],
     view: [Function: View],
     views: '/home/elsa/Projects/rental-borg/project/views',
     'jsonp callback name': 'callback',
     'view engine': 'handlebars',
     port: 8765 },
  flash: { info: undefined, error: undefined },
  _locals: { flash: { info: undefined, error: undefined } },
  cache: false }
15
15
16
16
16
16
...
23
23
23
23


很明显,当this../this相等时,我想../this实际上会返回类似../../this的视图对象本身。在我的特定情况下,这两次发生的时间分别是00:00和15:15。

这是一个错误吗?

编辑:我用把手3的新块参数功能解决了问题,但最初的问题仍然存在。这是我为解决问题所做的:

.handlebars文件:

{{#for 0 23 1 as |hour|}}
    {{#for 0 45 15 as |minute|}}
        <span>{{hour}}:{{minute}}</span>
    {{/for}}
{{/for}}


和助手:

for: function(from, to, incr, block) {
    var args = [], options = arguments[arguments.length - 1];
    for (var i = 0; i < arguments.length - 1; i++) {
        args.push(arguments[i]);
    }

    var accum = '';
    for(var i = from; i <= to; i += incr) {
        accum += options.fn(i, {data: options.data, blockParams: [i]});
    }
    return accum;
},

最佳答案

这是一个有趣的发现,我真的很想找到这种看似奇怪的行为的原因。我对Handlebars source code进行了深入研究,发现在line 176 of lib/handlebars/runtime.js上可以找到相关的代码。

Handlebars维护一堆上下文对象,其顶部对象是当前正在执行的Handlebars模板块中的{{this}}引用。当执行模板中遇到嵌套块时,会将新对象推入堆栈。这就是允许如下代码的方式:

{{#each this.list}}
    {{this}}
{{/each}}


在第一行,this是一个具有名为“ list”的可枚举属性的对象。在第二行,thislist对象的当前迭代项。正如问题所指出的那样,Handlebars允许我们使用../访问堆栈中较深的上下文对象。在上面的示例中,如果要从list帮助器中访问#each对象,则必须使用{{../this.list}}

有了这个简短的摘要,问题仍然存在:当外部for循环的当前迭代的值等于内部for循环的当前迭代的值时,为什么我们的上下文堆栈似乎中断了?

来自Handlebars源的relevant code是以下内容:

let currentDepths = depths;
if (depths && context != depths[0]) {
  currentDepths = [context].concat(depths);
}


depths是上下文对象的内部堆栈,context是传递给当前正在执行的块的对象,并且currentDepths是可供执行块使用的上下文堆栈。如您所见,仅当context不是loosely equal到堆栈的当前顶部context时,当前的depths[0]才被压入可用堆栈。

让我们将此逻辑应用于问题中的代码。

当外部#for块的上下文是15并且内部#for块的上下文是0时:


depths是:[15, {ROOT_OBJECT}](其中{ROOT_OBJECT}表示
对象,它是模板方法调用的参数。)
因为0 != 15currentDepths变为:[0, 15, {ROOT_OBJECT}]
在模板的内部#for块中,{{this}}
0{{../this}}15并且{{../../this}}是{ROOT_OBJECT}。


但是,当外部和内部#for块的上下文值分别为15时,我们得到以下信息:


depths是:[15, {ROOT_OBJECT}]
因为15 == 15currentDepths = depths = [15, {ROOT_OBJECT}]
在模板的内部#for块中,{{this}}15{{../this}}是{ROOT_OBJECT},而{{../../this}}undefined


这就是为什么当外部和内部{{../this}}块具有相同值时,您的#for似乎跳过一个级别的原因。实际上是因为内部#for的值未推送到上下文堆栈!

在这一点上,我们应该问为什么把手具有这种行为并确定这是功能还是错误。

碰巧的是,有意添加了此代码来解决用户使用车把时遇到的issue问题。该问题可以通过示例来证明:

假设一个上下文对象:

{
    config: {
        showName: true
    },
    name: 'John Doe'
}


用户发现以下用例违反直觉:

{{#with config}}
    {{#if showName}}
        {{../../name}}
    {{/if}}
{{/with}}


特定的问题是必须使用双../来访问根对象:{{../../name}}而不是{{../name}}。用户认为,由于{{#if showName}}中的上下文对象是config对象,因此升一级../应该访问config的“父级”-根对象。之所以需要两个步骤,是因为Handlebars正在为每个块助手创建一个上下文堆栈对象。这意味着到达根上下文需要两个步骤。第一步获取{{#with config}}的上下文,第二步获取根的上下文。

做了commit可以防止当新的上下文对象与上下文堆栈顶部的对象松散地相等时,将上下文对象推送到可用的上下文堆栈。负责的代码是我们在上面查看的源代码。从Handlebars.js的version 4.0.0开始,我们的配置示例将失败。现在只需要一个../步骤。

回到原始问题中的代码示例,将外部15块中的#for确定为等于内部15块中的#for的原因是由于在JavaScript;如果两个数字类型的对象的值相同,则它们相等。这与“对象”类型相反,“对象”类型仅当两个对象引用内存中的同一对象时才相等。这意味着,如果我们重新编写原始示例代码以将Object类型而不是Number类型用于上下文,那么我们将永远不会满足条件比较语句,并且始终在内部#for块中拥有预期的上下文堆栈。

我们的助手中的for循环将被更新,以将Object类型作为其创建的框架的上下文传递:

for(var i = from; i <= to; i += incr) {
    accum += block.fn({ value: i });
}


现在,我们的模板将需要访问该对象的相关属性:

{{log ../this.value}}
<span>{{../this.value}}:{{this.value}}</span><br>


通过这些编辑,您应该发现您的代码按预期执行。

声明这是否是Handlebars的错误有点主观。有条件地添加了条件,并且所产生的行为执行了预期的操作。但是,我很难想象当涉及的上下文是基元而不是对象类型时,这种行为是预期的还是期望的。使Handlebars代码仅对Object类型进行比较可能是合理的。我认为open an issue这里有一个合理的案例。

关于node.js - ../this在父级和子级具有相同值时在内部循环内返回 View 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40935524/

相关文章:

javascript - 添加 bodyParser() 挂起所有 api 请求

html - 有没有一种简单的方法来设计 html Handlebars 模板

loops - Handlebars : Get index of an item when there are two rows of items

jquery - 我如何使用签名上传到cloudinary

javascript - 如何让show Route在node.js/mongoDB应用程序中显示申请人姓名?

angularjs - 在 Rest API 中使用 Facebook 在 Express 和 NodeJS 中维护 JWT 的 key 和访问 token

node.js - Mongoose 批量插入错误

node.js - 类型为 '(req: Request, res: IResponse, next: NextFunction) => void' 的参数不可分配给类型为 'PathParams' 的参数与 express.js

node.js - Node.js中如何使用express区分HTTP请求和XML HTTP请求?

handlebars.js - 如何在 mustache 模板中添加评论?