【问题标题】:How to avoid template wrapping with empty div in Backbone?如何避免在 Backbone 中使用空 div 包装模板?
【发布时间】:2012-11-28 09:37:12
【问题描述】:

当我创建视图主干时,如果未设置 el,则会创建空的 div 容器。模板 (this.$el.html(this.template(this.model.toJSON()))) 插入到该 div 中。如何避免这个包装?我需要没有任何包装的干净模板,这样我就可以将它插入到我想要的任何地方?用很多元素调用jobView.$e.children() 是不合理的。

<script id="contactTemplate" type="text/html">
                <div class="job">
                    <h1><%= title %>/<%= type %></h1>
                    <div><%= description %></div>
                </div>     
</script>     

var JobView = Backbone.View.extend({
        template:_.template($("#contactTemplate").html()),

        initialize:function () {
            this.render();
        },
        render:function () {
            this.$el.html(this.template(this.model.toJSON()));
            return this;
        }
});

var jobView = new JobView({
   model:jobModel
});          

console.log(jobView.el);

【问题讨论】:

标签: javascript jquery backbone.js


【解决方案1】:

我认为这个问题的真正答案还没有提供,只需从模板中删除div 并将className 属性添加到JobView!这将产生您需要的标记:

模板:

<script id="contactTemplate" type="text/html">
     <h1><%= title %>/<%= type %></h1>
     <div><%= description %></div>
</script>

观点:

var JobView = Backbone.View.extend({
            className: 'job', // this class will be added to the wrapping div when you render the view

            template:_.template($("#contactTemplate").html()),

            initialize:function () {
                this.render();
            },
            render:function () {
                this.$el.html(this.template(this.model.toJSON()));
                return this;
            }
    });

当您调用 render 时,您最终会得到所需的标记:

<div class="job">
  <h1><%= title %>/<%= type %></h1>
  <div><%= description %></div>
</div>

【讨论】:

  • 但是如果你的模板有多个 div 而不是一个包含所有内容的外部 div 呢?
    ...
    ...
    之类的东西。此解决方案不起作用。
  • @Vic 不,不会,但这种情况不是问题所在,即使是这样,您也只需将内部 div 移动到模板中。主干视图只能有一个元素,所以无论如何这都是一个有争议的问题。
  • 我讨厌 Backbone 这样做。为什么模板 html 和视图定义中都有模板代码?
  • @Matt 我也是,我认为这是一个荒谬的设计决定
【解决方案2】:

我认为最好的解决方案是:

render: function(){
    var html = this.template(this.model.toJSON()));
    var newElement = $(html)
    this.$el.replaceWith(newElement);
    this.setElement(newElement);
    return this;
}

【讨论】:

  • this.$el.replaceWith 在这种情况下是没用的,所有这些都可以用单行替换:this.setElement(this.template(this.model.toJSON())));
  • 但这取决于父视图如何处理这个视图的渲染。您的方式使其具有幂等性,这非常适合重新渲染而不会破坏视图。
【解决方案3】:

抱歉,回复晚了,这可能已经解决了。我发现最好尝试尽可能轻松地制作模板和视图。我将 Handlebars 用于我的模板。

无论使用何种模板,您的每个模板都需要有一个与之关联的 HTML 元素,因此在您的视图中选择您想要的任何元素并将该元素从您的模板中删除,而不是试图违背常规。然后,您可以在视图中设置属性以反映已删除模板元素的属性。

var yourview = Backbone.View.extend({
{
     tagName    : *whatever element ( i.e. section, div etc ),
     attributes : {
           id    : 'your element id'
           class : 'your element class'
           etc
     },
})

然后在您的模板中删除该元素,此元素将在不包装模板的情况下很好地创建,而不必更改您的渲染方法。

【讨论】:

  • 这种方法比手动 HTML 操作更简洁,谢谢!
  • 这个清洁剂怎么样?我必须在 HTML 中指定模板/布局代码,现在在我的视图代码中?这很混乱。
【解决方案4】:

您可以将视图渲染到您喜欢的任何容器中,您可以自己指定 $el 属性,也可以在调用 render 之前使用 setElement() 方法:

var jobView = new JobView({
  model:jobModel
});          

jobView.setElement($('#your_selector_here'));

console.log(jobView.el);

如果您查看 View.el 的文档,您会发现您还可以在编写视图时或通过在构造函数中传递参数来指定 el 属性。

【讨论】:

  • 需要有模板jobView.el:&lt;div class="job"&gt;..&lt;/ div&gt;而不是&lt;div&gt;&lt;div class="job"&gt;&lt;/ div&gt;&lt;/ div&gt;
  • 这实际上是这里最好的解决方案之一,只是缺少视图中的渲染和模板,但这可以猜到。
【解决方案5】:

Backbone 也有同样的问题。在我看来,这是一个设计缺陷。这是一篇 Reddit 帖子,描述了一些可能的解决方案:http://www.reddit.com/r/javascript/comments/11pkth/how_do_you_render_your_backbonejs_views/

以下是 Jeremy Ashkenas 对此问题的看法:

|如果我想将 HTML 完全封装在我的模板中,而不创建任何额外的 div,|我必须替换 this.el。至少据我所知。有没有更好的方法来做到这一点?

放弃你这样做的愿望,你会过得更轻松;) Backbone 始终为您提供视图元素(“el”)的一个重要部分是,您的事件始终有效——无论视图是否在 DOM 中,数据是否已准备好,或者如果模板可用。这是一种更无状态的方式来声明鼠标和键盘事件,更少依赖渲染所需的顺序。 如果您真的想替换视图的元素,请使用 setElement。但不推荐,也没有必要。

【讨论】:

  • 将所有视图封装到一个元素中并不是设计缺陷,而是一种设计优势。请注意,您可以将此元素指定为您想要的任何内容,而不仅仅是 div :)
  • 当我使用模板时,我已经将它们封装到一个元素中。将模板(任何类型,mustache,handlebars.js,等等)加载到视图中应该是客户端 Web 应用程序框架的第二天性。我看到 Jeremy 的观点是让事件始终有效,但是为什么在实例化视图时不能调用模板?
  • +1 为那林。这有效地将您的布局代码在模板 HTML 和 View JavaScript 之间拆分。处理表格时既不酷又烦人。
【解决方案6】:

无论好坏,在 Backbone 中,您都应该使用视图提供的 el,并使用该视图的 tagNameclassNameidattributes 属性根据自己的喜好调整它查看。

这种方法的明显问题是你混合和匹配 JS 和 HTML 就像没有明天一样。在我看来,最好把东西很好地分开; el 的特征应与模板一起指定。

2014 年,我编写了一个旨在解决该问题的插入式组件:Backbone.Declarative.Views。它可能是我所有的开源东西中最被低估的,但它做得很好。

怎么做?

Include Backbone.Declarative.Views 进入您的项目。然后,将那些el-定义属性放入模板的数据属性中。

<script id="my-template"
        type="text/x-template"

        data-tag-name="p"
        data-id="myContainer"
        data-class-name="someClass orOther">

    <!-- template content here -->

</script>

现在,如果您的视图具有 template: "#my-template" 属性,则其 el 设置为

<p id="myContainer" class="someClass orOther"></p>

就是这样,真的。详情请查看the docs

为什么选择数据属性?

使用数据属性解决了将与 HTML 相关的内容保留在 HTML 文件和模板中以及在 Javascript 之外的基本问题。同时,这种方法是fully compatible 与通常在 Backbone 中完成的方式一样。

也就是说,所有现有的 Backbone 模板都可以继续正常工作,就像散布在 Javascript still get applied as usual 中的所有 tagName 定义一样。避免了与其他 Backbone 扩展和插件的冲突,并且无需更改遗留代码。这使得 Backbone.Declarative.Views 可以安全地包含在几乎任何项目中。

【讨论】:

  • setElement 方法使您能够将所有 HTML 内容保留在模板中,从而无需 tagName、id、className 和您的组件。无需使用数据属性扩展 HTML。
  • @EmileBergeron setElement 方法可以正常工作,如果您在页面 HTML 中有一个硬编码的节点可以参考。它不适用于动态生成的东西(想想列表元素)。为此,您必须使用附加代码准备节点,这又会将相关内容分散到 JS 和 HTML 的各个部分。
  • 其实你可以使用setElement来显示动态列表,通过像this.setElement(this.template)这样的列表项模板来代替this.$el.html(this.template)
  • 看看我的意思:stackoverflow.com/a/11598543/1218980 这正是渲染动态列表项的方式。
  • @EmileBergeron 每种方法都有其权衡。这太多了,无法在评论中讨论,但我不久前写过关于pros and cons of setElement() elsewhere 的文章。在某些情况下setElement() 是更好(或唯一)的选择,但我认为这是例外,而不是规则。
猜你喜欢
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-07
  • 1970-01-01
  • 2015-04-23
  • 1970-01-01
相关资源
最近更新 更多