30

我在这里查看 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 在渲染中关闭 div 换行

但我只能让它为 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被包裹起来<div>

我想要的是在一个地方(angry_cat-template)指定HTML,并且在angered_cat-template中没有大多数HTML(所有<td>标签),在AngryCatView中没有一点HTML(<tr>标签)。我想在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 工作。

有任何想法吗?

4

5 回答 5

42

2014 年 2 月 18 日 — 更新以适应 @vaughan 和 @Thom-Nichols 在评论中指出的改进


在我的许多 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()会返回<tr class="angry_cat">,所以这应该很完美。

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

需要注意的一件事:

这种技术不会只强制 1 个子元素。它盲目地抓取.children(),所以如果您错误地构建了模板以返回多个元素,例如第一个具有 3 个<td>元素的模板示例,它将无法正常工作。

它要求您的模板返回单个元素,就像您在带有根<tr>元素的第二个模板中所做的那样。

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


这是好奇的工作示例:http://codepen.io/somethingkindawierd/pen/txnpE

于 2013-02-04T03:24:01.947 回答
10

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

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

于 2013-02-03T20:27:26.443 回答
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
于 2014-02-10T13:03:15.713 回答
3

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

var Layout = Backbone.Marionette.LayoutView.extend({

  ...

  onRender: function () {
    this.setElement(this.el.innerHTML);
  }

  ...

});
于 2015-02-13T05:27:10.990 回答
1

对于 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;
      }
  }

  ...

});

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

于 2016-03-22T21:36:50.917 回答