代码之家  ›  专栏  ›  技术社区  ›  Eino Gourdin

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

  •  0
  • Eino Gourdin  · 技术社区  · 9 年前

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

    它正在工作,但是现在我需要在运行时动态引用对象,我不确定我见过的模板化解决方案(下划线、把手…)是否可能“扁平化”javascript。

    为了说明,我有一个对象列表,比如Tasks。 我有一个模型可以简化为:

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

    现在,我需要在渲染完成后动态使用“task”对象。例如,执行以下操作:

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

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

    有办法做到这一点吗?

    我在想我需要闭包来保存对象,获取它们的唯一方法不是将html变平,否则一切都将转换为字符串。

    知道吗?也许有模板库可以直接生成DOM对象,我可以将其添加到文档中?

    提前感谢,

    2 回复  |  直到 9 年前
        1
  •  1
  •   mu is too short    9 年前

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

    一种方法是使用数据属性来隐藏模型 id s、 您的模板如下所示:

    {{#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/

    所有代码都保留在视图中(其中所有数据都已存在),模板只处理表示。没有全局变量,良好的关注点分离,以及惯用的主干结构。

    如果视图的每个模型部分更复杂,则可以为集合创建一个视图,为每个模型创建子视图。在这种情况下,每个模型的模板如下所示:

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

    不需要更多数据属性。您将有一个新的每模型视图,如下所示:

    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 可以调用 remove 在所有孩子身上 虚拟机 s

        2
  •  1
  •   Community CDub    7 年前

    我的回答只是一个提示,我尽量接近这个问题。请参阅此答案以获得更好的解决方案: https://stackoverflow.com/a/32493586/1636522 .

    您可以使用索引或任何其他信息来查找项目。下面是一个使用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 StackOverflow.' };
    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>