20

使用 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

4 回答 4

13

每个主干视图必须由单个元素表示。您的第一个 HTML 块有两个元素,这就是为什么如果不先将其包装在外部 div 中就无法用视图表示的原因。

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

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

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

但是,我不确定如果您传入的 HTML 有两个父元素而不是只有一个,这将如何工作。

于 2012-07-06T16:52:36.470 回答
10

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

我喜欢使用 Twitter/Bootstrap 作为我的 UI 库,并且由于默认的标签包装(特别是<div>my<tbody>和 my <tr>s 之间的 a )而在表格样式方面遇到了一些问题。

经过一番挖掘,我在 Marionette 文档中找到了Region关于如何Viewel. Backboneel从各种View属性构建,并保持构建的元素是最新的,因此它可以随时呈现。所以我想,如果view.el是父级,为什么不直接使用 HTML 内容呢?Marionette 还提供了一种方法来创建自定义Region

我能够通过创建Region具有覆盖open功能的自定义来运行所有内容。这样我就可以指定我想用标签包装哪些区域以及我不想包装哪些区域。在下面的示例片段中,我创建了我的自定义非包装Region( ) 并在我的( ) 中NowrapRegion使用它作为我的(我在我的真实程序中传递的视图 create s)。LayoutMyLayout<tbody><tr>

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
  }
});

繁荣!想要的时候包,不想的时候不包。

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

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

EDIT2: @chris-neale 提出了以下建议,我还没有验证这一点,但它似乎是合理的。谢谢!

与其在视图中复制 html,不如在子节点上使用深度克隆将维护事件和数据。

var NowrapRegion = Marionette.Region.extend({
  open: function(view){
    view.$el.children().clone(true).appendTo(this.$el);
  }
});
于 2013-02-26T00:44:38.887 回答
6

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

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

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

更新:

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

使用 Backbone 视图的很大一部分优势在于,它们的元素始终可用——无论是否已呈现模板(许多视图具有多个模板)——无论视图是否存在于DOM 与否。

这允许您创建视图并将其添加到 DOM,稍后渲染,并确保您的所有事件都正确绑定(因为它们是从 view.el 委托的)。

如果你能想出一个好的策略,让视图拥有“完整”的模​​板,同时保留上述特性……那么我们来谈谈它——但它必须允许根元素存在而不必渲染模板(可能取决于稍后可能不会到达的数据),并且它必须允许视图轻松拥有多个模板。

于 2012-06-25T22:49:29.083 回答
1

我想实现几乎相同的目标。我想在我的模板中包含所有的 HTML 标记,让 Backbone 完成其余的工作。因此,我编写了以下代码段,删除了额外的“div”。

首先,您将 tagName 设置为“检测”,然后在第一次渲染后检测视图中第一个元素的 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>

但是,我只是稍微尝试了一下——如果有人看到这种方法可能导致的任何问题(性能、内存、僵尸等),我会非常愿意了解它们。

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

于 2013-03-15T18:07:57.657 回答