忽略下划线模板中未定义的数据/变量

Ignoring undefined data / vars in an underscore template

本文关键字:数据 变量 未定义 下划线      更新时间:2023-09-26

仍在学习骨干,请耐心等待;

我试图添加一个新的模型与空白字段的视图,但我已经创建的模板有一大堆

<input value="<%= some_value %>" type="whatever" />

在获取数据时工作得很好,它填充数据,一切都很顺利。当我想创建一个新的(空白)渲染视图时,问题出现了,它给了我

Uncaught ReferenceError: some_value is not defined

我可以设置defaults(我已经为一些在db中具有默认值的人做了),但这意味着键入超过40个空格;有更好的处理方式吗?

我正在摆弄下划线模板本身,尝试像<%= if(some_value != undefined){ some_value } %>这样的东西,但这似乎也有点麻烦。

在包装器对象中传递模板数据。缺少属性访问不会抛出错误:

所以,而不是:

var template = _.template('<%= foo %><%= bar %>');
var model = {foo:'foo'};
var result = template(model); //-> Error

试题:

var template = _.template('<%= model.foo %><%= model.bar %>');
var model = {foo:'foo'};
var result = template({model:model}); //-> "foo"

实际上,您可以在模板中使用arguments:

<% if(!_.isUndefined(arguments[0].foo)) { %>
       ...
<% } %>

由于下划线模板的实现方式,没有实际的修复

请看下面的讨论:

恐怕这就是JS中with(){}的工作方式。如果没有声明变量,它就是一个ReferenceError。在保留模板其他行为的同时,我们对此无能为力。

你可以完成你所寻找的唯一方法是用另一个对象包装对象,就像其他答案建议的那样,或者设置默认值。

如果检查生成的模板函数的源代码,您将看到如下内容:

with (obj||{}) {
  ...
  // model property is used as variable name
  ...
}

这里发生了什么:首先JS试图在"obj"中找到你的属性,这是模型(更多关于with语句)。这个属性在"obj"作用域中找不到,所以JS向上遍历直到全局作用域,最后抛出异常。

所以,你可以直接指定你的作用域来解决这个问题:
<input value="<%= obj.some_value %>" type="whatever" />

实际上,您可以像初始对象属性一样访问变量。

如果你将调试器激活到模板中,你可以找到变量"obj",它包含了你所有的数据。

所以你应该写<%= obj.title %>
而不是<%= title %>

lodash是一个下划线替代品,它提供了一个具有内置解决方案的模板函数。它有一个选项,可以将数据包装在另一个对象中,以避免导致错误的"with"语句。

API文档中的示例用法:

// using the `variable` option to ensure a with-statement isn’t used in the compiled template
var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' });
compiled.source;
// → function(data) {
//   var __t, __p = '';
//   __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!';
//   return __p;
// }

一个非常简单的解决方案:您可以确保您的数据收集是规范化的,即所有属性都存在于每个对象中(如果它们未使用则为空值)。下面的函数可以提供帮助:

function normalizeCollection (collection, properties) {
  properties = properties || [];
  return _.map(collection, function (obj) {
    return _.assign({}, _.zipObject(properties, _.fill(Array(properties.length), null)), obj);
  });
}

(注意:_.zipObject_.fill在lodash的最新版本中可用,但下划线不可用)

像这样使用:

var coll = [
  { id: 1, name: "Eisenstein"},
  { id: 2 }
];
var c = normalizeCollection(coll, ["id", "name", "age"]);
// Output =>
// [
//  { age: null, id: 1, name: "Eisenstein" },
//  { age: null, id: 2, name: null }
// ]

当然,你不需要永久地转换你的数据& & &;只需在调用模板呈现函数时动态调用该函数:

var compiled = _.template(""); // Your template string here
// var output = compiled(data); // Instead of this
var output = compiled(normalizeCollection(data)); // Do this 

您可以通过在模型中添加一个函数并在模板中使用它来进一步抽象@Dmitri的答案。

例如:

模型:

new Model = Backbone.Model.extend({
    defaults: {
        has_prop: function(prop) {
            return _.isUndefined(this[property]) ? false : true;
        }
    }
});

模板:

<% if(has_prop('property')) { %>
    // Property is available
<% } %>

正如他回答中的评论所暗示的那样,这是更可扩展的