使用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吗?
答案 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
,我能够让所有内容都正常运行。这样我可以指定我想用标签包装的区域和我不包含的区域。在以下示例代码段中,我创建了自定义非包装Region
(NowrapRegion
),并在Layout
(MyLayout
)中为我的<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>
然而,我只是玩了一下 - 如果有人发现这种方法可能造成任何问题(性能,记忆,僵尸等),我会非常乐于了解它们。
顺便说一句,这可能很容易打包成插件。