Javascript 允许渲染后使用注入对象的模板解决方案?

Javascript templating solution that allows after-rendering use of injected objects?

所以,我正在构建一个基于 Backbone.js 的应用程序,使用模板渲染一些对象。

它正在工作,但是现在我需要在运行时动态引用对象,而且我不确定我所见过的模板解决方案(下划线、把手等)是否可行,"flatten" javascript.

为了说明,我有一个对象列表,比方说任务。 我有一个可以这样简化的模型:

{{#each tasks.models as |task|}}
<div>
{{task.name}}
</div>
{{/each}}

现在,我需要在渲染完成后动态使用 'task' 对象。例如,做这样的事情:

<div>
{{task.name}} - <button onClick="task.setComplete()" />
</div>

当然这种方式不行;也不会做类似 {{task}}.setComplete() 的事情,因为 {{task}} 在渲染时被转换为字符串。

有办法吗?

我在想我需要闭包来保留对象,获得它们的唯一方法是不展平 html,否则所有内容都会转换为字符串。

有什么想法吗?也许有允许直接生成 DOM 对象的模板库,我可以将其添加到我的文档中?

提前致谢,

我的回答只是一个提示,我尽量贴近问题。请参阅此答案以获得更好的解决方案:.

您可以使用索引或任何其他信息来查找项目。下面是一个使用 Handlebars 的例子,假设每个任务都可以通过一个 id 来识别:

var tasks = [];
var source = $('#source').html();
var template = Handlebars.compile(source);
tasks[0] = { id: 42, label: 'coffee', description: 'Have a cup of coffee.' };
tasks[1] = { id: 13, label: 'help', description: 'Connect to Whosebug.' };
tasks[2] = { id: 40, label: 'smile', description: 'Make μ smile.' };
$('#placeholder').html(template({ tasks: tasks }));

function onClick (id) {
  var task, i = 0, l = tasks.length;
  while (i < l && tasks[i].id !== id) i++;
  if (i === l) {
    // not found
  }
  else {
    task = tasks[i];
    alert(task.label + ': ' + task.description);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.2/handlebars.min.js"></script>
<script id="source" type="text/x-handlebars-template">
  {{#each tasks}}
  <button 
    type="button" 
    class="task" 
    style="margin-right:.5em"
    onclick="onClick({{id}})"
  >{{label}}</a>
  {{/each}}
</script>
<div id="placeholder"></div>

此问题带有 标记,因此您应该使用 Backbone 的普通视图事件处理系统而不是 onclick 处理程序。你提到 tasks.models 所以大概 tasks 是一个集合。

一种方法是使用 data-attributes 来存储模型 id。您的模板将如下所示:

{{#each tasks}}
    <div>
        {{name}} - <button data-id="{{id}}" type="button">Completed</button>
    </div>
{{/each}}

然后您的视图将设置如下:

Backbone.View.extend({
    events: {
        'click button': 'completed'
    },
    render: function() {
        var t = Handlebars.compile($('#whatever-the-template-is').html());
        this.$el.append(t({
            tasks: this.collection.toJSON()
        }));
        return this;
    },
    completed: function(ev) {
        var id = $(ev.currentTarget).data('id');
        var m  = this.collection.get(id);
        // Do whatever needs to be done to the model `m`...
    }   
});

演示: https://jsfiddle.net/ambiguous/z7go5ubj/

所有代码都保留在视图中(所有数据都已经存在),模板只处理演示。没有全局变量、很好的关注点分离和惯用的 Backbone 结构。

如果视图的 per-model 部分更复杂,那么您可以为集合使用一个视图,为每个模型使用子视图。在这种情况下,您的 per-model 模板将如下所示:

<div>
    {{name}} - <button type="button">Completed</button>
</div>

不再需要 data-attribute。您将有一个新的 per-model 视图,如下所示:

var VM = Backbone.View.extend({
    events: {
        'click button': 'completed'
    },
    render: function() {
        var t = Handlebars.compile($('#whatever-the-template-is').html());
        this.$el.append(t(this.model.toJSON()));
        return this;
    },
    completed: function() {
        console.log('completed: ', this.model.toJSON());
    }   
});

循环将移动到集合视图:

var VC = Backbone.View.extend({
    render: function() {
        this.collection.each(function(m) {
            var v = new VM({ model: m });
            this.$el.append(v.render().el);
        }, this);
        return this;
    }
});

演示: https://jsfiddle.net/ambiguous/5h5gwhep/

当然在现实生活中你的 VC 会跟踪它的 VM 以便 VC#remove 可以在它的所有 child 上调用 remove VMs.