【问题标题】:Turning off div wrap for Backbone.Marionette.ItemView关闭 Backbone.Marionette.ItemView 的 div 换行
【发布时间】:2013-01-17 08:12:35
【问题描述】:

我在这里查看 Angry Cats Backbone/Marionette 教程帖子

http://davidsulc.com/blog/2012/04/15/a-simple-backbone-marionette-tutorial/

http://davidsulc.com/blog/2012/04/22/a-simple-backbone-marionette-tutorial-part-2/

我在这里发布了同样的问题/需求:

Backbone.js turning off wrap by div in render

但我只能让它为 Backbone.Views 工作,而不是 Backbone.Marionette.ItemViews。

例如,从上面简单的骨干木偶教程链接中,取 AngryCatView:

AngryCatView = Backbone.Marionette.ItemView.extend({
  template: "#angry_cat-template",
  tagName: 'tr',
  className: 'angry_cat',
  ...
});

模板#angry_cat-template 如下所示:

<script type="text/template" id="angry_cat-template">
  <td><%= rank %></td>
  <td><%= votes %></td>
  <td><%= name %></td>
  ...
</script>

我不喜欢的是 AngryCatView 需要有

  tagName: 'tr',
  className: 'angry_cat',

-- 如果我取出tagName,那么angry_cat-template 会被&lt;div&gt; 包裹。

我想要的是在一个地方(angry_cat-template)指定 HTML,而不是在angry_cat-template 中包含大多数 HTML(所有 &lt;td&gt; 标签)和在愤怒的猫视图。我想在angerous_cat-template中写这个:

<script type="text/template" id="angry_cat-template">
  <tr class="angry_cat">
    <td><%= rank %></td>
    <td><%= votes %></td>
    <td><%= name %></td>
    ...
  </tr>
</script>

这对我来说只是感觉更干净,但我一直在纠结 Derik Bailey 在“Backbone.js 关闭渲染中的 div 换行”中的回答,并且无法让它为 Backbone.Marionette 工作。

有什么想法吗?

【问题讨论】:

    标签: javascript backbone.js marionette


    【解决方案1】:

    2014/02/18 — 更新以适应 @vaughan 和 @Thom-Nichols 在 cmets 中指出的改进


    在我的许多 itemView/布局中,我这样做:

    var Layout = Backbone.Marionette.Layout.extend({
    
        ...
    
        onRender: function () {
            // Get rid of that pesky wrapping-div.
            // Assumes 1 child element present in template.
            this.$el = this.$el.children();
            // Unwrap the element to prevent infinitely 
            // nesting elements during re-render.
            this.$el.unwrap();
            this.setElement(this.$el);
        }
    
        ...
    
    });
    

    上述代码仅在包装器 div 包含单个元素时有效,这就是我设计模板的方式。

    在您的情况下,.children() 将返回 &lt;tr class="angry_cat"&gt;,所以这应该可以完美运行。

    我同意,它确实使模板更加简洁。

    需要注意的一点:

    此技术不会只强制使用 1 个子元素。它盲目地抓取.children(),因此如果您错误地构建了模板以返回多个元素,例如第一个带有3 个&lt;td&gt; 元素的模板示例,它将无法正常工作。

    它要求您的模板返回单个元素,就像您在第二个模板中使用根 &lt;tr&gt; 元素一样。

    当然,如果需要,也可以编写它来测试。


    Here is a working example for the curious: http://codepen.io/somethingkindawierd/pen/txnpE

    【讨论】:

    • @somethingkindaweird 仅使它有一个孩子的限制是什么?我只是想在实现之前更好地理解代码。
    • @streetlight — 我澄清了关于模板中需要 1 个根元素的答案。编写的 javascript 代码不强制执行此操作。它要求模板返回一个包装内容的根元素,在原始问题的情况下,一个根 &lt;tr&gt; 元素。
    • 如果您重新渲染,这会导致问题。新的 el 将用作重新渲染创建模板和重新渲染无限嵌套结构的容器。您应该覆盖render。我在下面发布了解决方案。
    • @vaughan 我发现在this.$el = this.$el.children() 之后添加this.$el.unwrap() 可以解决重新渲染的问题,而无需重新定义整个render 方法。
    • 谢谢@thom-nicols 和 vaughan。我已经更新了答案以包含更正。
    【解决方案2】:

    虽然我确信有一种方法可以破解 render 的内部结构以使其按照您想要的方式运行,但采用这种方法意味着您将在整个开发过程中与 Backbone 和 Marionette 的约定作斗争过程。 ItemView 需要有一个关联的$el,按照惯例,它是一个div,除非你指定一个tagName

    我很同情——尤其是在布局和区域的情况下,似乎不可能阻止 Backbone 生成额外的元素。我建议您在学习框架的其余部分时接受约定,然后才决定是否值得破解 render 以改变行为(或只是选择不同的框架)。

    【讨论】:

    • 我绝对明白你的意思——不要与框架抗争。框架原样允许一个顶级元素,也允许 N 个顶级元素,这是灵活的,这很好,但是随后包装元素(div 或 tagName)开始发挥作用,我想看看它是否可配置。我喜欢你的回答,谢谢 - 我要检查 @something 因为它包含我现在可以使用的代码
    【解决方案3】:

    此解决方案适用于重新渲染。您需要覆盖render

    onRender 技巧不适用于重新渲染。它们会在每次重新渲染时导致嵌套。

    BM.ItemView::render = ->
      @isClosed = false
      @triggerMethod "before:render", this
      @triggerMethod "item:before:render", this
      data = @serializeData()
      data = @mixinTemplateHelpers(data)
      template = @getTemplate()
      html = Marionette.Renderer.render(template, data)
    
      #@$el.html html
      $newEl = $ html
      @$el.replaceWith $newEl
      @setElement $newEl
    
      @bindUIElements()
      @triggerMethod "render", this
      @triggerMethod "item:rendered", this
      this
    

    【讨论】:

    • 由于 OP 是用 JavaScript 编写的,如果您的答案也是用 JavaScript 编写的,那将会很有帮助。 IMO 这应该是公认的答案,因为您对 reRender 的看法确实是正确的,但遗憾的是,coffeescript 很糟糕。
    【解决方案4】:

    使用 vanilla JS 而不是 jQuery 来完成这个不是更干净吗?

    var Layout = Backbone.Marionette.LayoutView.extend({
    
      ...
    
      onRender: function () {
        this.setElement(this.el.innerHTML);
      }
    
      ...
    
    });
    

    【讨论】:

    • 其实视情况而定,这可能会导致模板中的元素丢失(当它有多个时),更不用说它们的属性(由于使用了innerHTML)。不是 OP 的情况,但如果您的模板可以包含多个元素并且您需要通用解决方案,请参阅我的答案。
    【解决方案5】:

    对于 IE9+,你可以使用 firstElementChildchildElementCount

    var Layout = Backbone.Marionette.LayoutView.extend({
    
      ...
    
      onRender: function () {
          if (this.el.childElementCount == 1) {
              this.setElement(this.el.firstElementChild);
          }
      }
    
      ...
    
    });
    

    Marionette 自动插入包装器 DIV 是有充分理由的。只有当您的模板仅包含一个元素时,您才能删除它。因此测试子元素的数量。

    另一种选择是使用每个 Marionette 视图中的 attachElContent 方法。它的默认实现意味着视图的重新渲染将覆盖根元素的内部 HTML。这最终导致了 bejonbee 的答案中提到的无限嵌套。

    如果您不想覆盖 onRender 和/或需要纯 JS 解决方案,以下代码可能正是您想要的:

    var Layout = Backbone.Marionette.LayoutView.extend({
    
      ...
    
      attachElContent: function (html) {
          var parentEl = this.el.parentElement;
          var oldEl;
    
          //View already attached to the DOM => re-render case => prevents
          //recursive nesting by considering template's top element as the
          //view's when re-rendering
          if (parentEl) {
              oldEl = this.el;
              this.setElement(html);                   //gets new element from parsed html
              parentEl.replaceChild(this.el, oldEl);   //updates the dom with the new element 
              return this;
    
          //View hasn't been attached to the DOM yet => first render 
          // => gets rid of wrapper DIV if only one child
          } else {
              Marionette.ItemView.prototype.attachElContent.call(this, html);
              if (this.el.childElementCount == 1) {
                  this.setElement(this.el.firstElementChild);
              }
              return this;
          }
      }
    
      ...
    
    });
    

    请注意,为了使重新渲染正常工作,代码假定模板具有包含所有标记的单个子级。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-15
      • 2016-07-25
      • 2020-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-11
      相关资源
      最近更新 更多