Backbone和Marionette的额外包装

时间:2012-06-25 18:36:29

标签: backbone.js marionette

使用Backbone和Marionette,我创建了一个新的布局,进入我页面上的主要内容div。布局如下所示:

<div id='dash-sidebar'>
    <div id='dash-profile'></div>
    <div id='dash-nav'></div> 
</div>
<div id='dash-content'></div>

问题在于,当我渲染布局时,Backbone会自动将其包装在div中,然后将其放入主内容div中,如下所示:

<div id='main-content'>    
  <div>
    <div id='dash-sidebar'>
      <div id='dash-profile'></div>
       <div id='dash-nav'></div> 
    </div>
    <div id='dash-content'></div>
  </div>
</div>

我知道我可以使用tagName更改元素,但是可以避免完全包装模板并将其直接插入页面上的主内容div吗?

4 个答案:

答案 0 :(得分:13)

每个Backbone View必须由单个元素表示。你的第一个HTML块有两个元素,这就是为什么它没有先将它包装在外部div中就无法用视图表示。

您是否可以重构布局以包含main-content区域?然后布局的el将对应于整个外部div。

要尝试的另一件事是使用Backbone.View的setElement()方法来覆盖外部div的创建,并手动为视图中的元素注入所需的HTML。类似的东西:

onRender: function() {
    this.setElement( /* the HTML you want for your layout */ );
}

我不确定如果你传入的HTML有两个父元素而不是一个,那么这是怎么回事。

答案 1 :(得分:10)

EDIT3 :警告!这个答案可能已经过时了。我收到一条评论说这个答案不再适用,没有时间调查(我个人不使用这种方法)。

我喜欢使用Twitter / Bootstrap作为我的UI库,并且由于默认标记换行(特别是<div><tbody>之间的<tr>,因此表格样式存在一些问题或多个)。

经过一番挖掘后,我在Region关于how the View attaches the el的木偶文档中找到了一些内容。来自各种View属性的Backbone builds the el并使构建的元素保持最新,以便可以随时呈现。所以我想,如果view.el是父,为什么不使用HTML内容呢? Marionette还提供了create a custom Region

的方法

通过创建具有重写Region功能的自定义open,我能够让所有内容都正常运行。这样我可以指定我想用标签包装的区域和我不包含的区域。在以下示例代码段中,我创建了自定义非包装RegionNowrapRegion),并在LayoutMyLayout)中为我的<tbody>使用它(我在实际程序中传递的视图创建<tr> s)。

var NowrapRegion = Marionette.Region.extend({
  open: function(view){
    this.$el.html(view.$el.html());
  }
});

var MyLayout = Marionette.Layout.extend({
  template: templates.mylayout,
  regions: {
    foo: '#foo',
    columns: '#columns', //thead
    rows: { selector: '#rows', regionType: NowrapRegion } //tbody
  }
});

BOOM!当我想要它时包装,而在我不需要时包装。

编辑:这似乎会影响events级别应用的*View。我还没有调查过,但要注意它们似乎没有被触发。

这是否是“正确”的做法,我不确定。如果他看到这个,我会欢迎来自@ derick-bailey的任何反馈。

EDIT2: @ chris-neale建议如下,我还没有验证这个,但似乎听起来不错。谢谢!

  

不是在视图中复制html,而是使用深层克隆   子节点将维护事件和数据。

var NowrapRegion = Marionette.Region.extend({
  open: function(view){
    view.$el.children().clone(true).appendTo(this.$el);
  }
});

答案 2 :(得分:6)

更新: Marionette.Layout是Backbone视图的扩展,所以这是正常行为,请参阅主干文档:

  

“el”属性引用在浏览器中创建的DOM对象。   每个Backbone.js视图都有一个“el”属性,如果没有定义,   Backbone.js将构造自己的,这是一个空的div元素。

所以我之前的回答与你的问题无关,抱歉。

更新

在Backbone gitHub上发现issue 546关于这个主题(关闭为wontfix),jashkenas发布了这条评论来解释为什么它不容易实现:

  

使用Backbone Views的很大一部分优势在于   他们随时都有自己的元素 - 无论如何   是否已呈现模板(许多视图具有多个   模板) - 无论DOM中是否存在视图   不

     

这允许您为DOM创建和添加视图,稍后呈现,   并确保所有事件都正确绑定(因为   他们是从view.el)委派的。

     

如果你能想出一个允许视图拥有的好策略   “完整”模板,同时保留上述特征......   然后让我们谈谈它 - 但它必须允许根元素   存在而不必渲染模板的内容(可能   依赖于可能在以后才能到达的数据,并且必须允许a   查看以轻松拥有多个模板。

答案 3 :(得分:1)

我想要实现几乎相同的事情。我想在我的模板中包含所有HTML标记,然后让Backbone完成剩下的工作。所以我写了下面的代码片段,删除了额外的'div'。

首先,将tagName设置为'detect',然后在第一次渲染后检测视图中第一个元素的tagName。显然,这仅在您在模板中提供自己的包装时才有效。

// Single table row inside tbody
tableRowView = Backbone.Marionette.ItemView.extend({
  tagName: 'detect',
  template: function(data) {
    return Handlebars.templates['tablerow/single'](data);
  },
  onRender: function() {
    if (this.tagName == 'detect')
      this.tagName = this.$el.children().first().prop('tagName').toLowerCase();
    this.setElement( this.$el.children( this.tagName ) );
    var $parent = this.$el.parent( this.tagName );
    if ($parent.length) {
      this.$el.detach();
      this.$el.insertAfter($parent);
      $parent.remove();
    }
  },
  modelEvents: {
    "change": "render"
  }
});

模板:

<tr>
  <td>{{artist}}</td>
  <td>{{title}}</td>
  <td>{{length}}</td>
</tr>

然而,我只是玩了一下 - 如果有人发现这种方法可能造成任何问题(性能,记忆,僵尸等),我会非常乐于了解它们。

顺便说一句,这可能很容易打包成插件。