我正在为联系人管理器在骨干网.js中实现视图/编辑视图。 Web 建议创建一个具有子视图的 Contact 类,称为 ContactView 和 ContactEdit。 问题是,这些需要在DOM中占据相同的el,所以我不能将子项嵌套在父项中。 这是因为从外面看,我希望家长的观点只提到联系人,就好像孩子是私人的一样。 我试过这个,它第一次渲染时有效():
initialize: function() {
this.view[0] = new CL.Views.ContactView({model: this.model, el: this.el});
this.view[1] = new CL.Views.ContactEdit({model: this.model, el: this.el});
},
render: function() {
this.view[0].render();
return this;
}
但是我无法交换视图。 我尝试了这个.view[0].remove()和我能想到的一切,但就是无法使用相同的el让浏览和编辑视图相互交换。
我认为这意味着最好在一个视图中有两个模板并交换它们,这已经大部分有效。 我认为它是骨干.js不能很好地处理 DOM 中同一级别视图的继承。
我宁愿避免使用主干.js扩展,但愿意遵循它们实现的任何模式。 我正在尝试以"正确"的方式执行此操作,因为查看/编辑是我们应用程序中表单的常见模式。
附言说明此问题的另一种方法是,如果没有父视图包围它们,如何隐藏视图并将其替换为 backbone.js 中的另一个视图?
提前感谢您提供的任何帮助。
我不太明白您是否想查看Models
Collection
,还是想要处理单个Model
的视图?
如果是单个模型的视图,
然后你可以坚持一个视图。只需让一些事件侦听您的操作即可启用或禁用编辑功能。(即使通过在 dom 元素上设置 contenteditable="true",您也可以做到这一点)
我强烈建议使用一些工具,如backbone.marionette或chaplinjs。他们将为您节省大量时间。
以下示例适用于 Backbone.Marionette
示例模板
<script type="text/template" id="contact-view-template">
<span data-bind="firstName"></span>
<span data-bind="lastName"></span>
<span data-bind="email"></span>
<a href="#" data-action="edit">
<a href="#" data-action="save" style="display:none">
</script>
视图代码:
ContactView = Marionette.ItemView.extend({
template:"#contact-view-template",
events:{
"click [data-action=edit]":"edit",
"click [data-action=save]":"save"
},
edit:function(){
this.$("[data-action=edit]").hide();
this.$("[data-action=save]").show();
this.$("[data-bind]").attr("contenteditable",true);
},
save:function(){
var value = {};
_.each(this.$("[data-bind]",function(el){
value[el.dataset['bind']]= $(el).val() || $(el).text();
}));
this.model.set(value);
// add your validation here
this.model.save();
}
});
如果要拥有多个编辑视图,而不仅仅是:
ContactListEditView = Marionette.CompositeView.extend({
itemView:ContactView.extend({
tagName:"li"
}),
itemViewContainer:"ul",
template:_.template("<h1>ContactList</h1><p>feel free to edit</p><ul></ul>")
});
如果您需要 1 个编辑视图和多个不可编辑视图
(我希望我没有犯任何严重的错误):
ContactEditView = Marionette.ItemView.extend({
template:"#contact-edit-view", // your template & bindings
events:{
"click [data-action=save]":"save"
},
save:function(){
var value = {};
_.each(this.$("[data-bind]",function(el){
value[el.dataset['bind']]= $(el).val() || $(el).text();
}));
this.model.set(value);
this.model.save();
}
});
ContactListView = Marionette.CompositeView.extend({
itemView:Marionette.ItemView.extend({
template:"#contact-view-template",
events:{
"click [data-action=edit]":"edit"
},
edit:function(){
this.trigger("edit",this);
}
}),
regions:{
"edit":"[data-region=edit]"
},
initialize:function(){
this.on("itemview:edit",function(view){
this.edit.show(new ContactEditView({model:view.model}));
}.bind(this));
}
});
我认为您的问题源于您的父视图与子视图共享相同元素的事实。当您呈现ContactView
或ContactEdit
视图时,它会替换 DOM 元素,当您remove
子视图时,它(根据定义)也会删除父视图元素,因为它们是同一元素。
相反,您应该撰写父视图,以便将子视图呈现到容器元素中。类似的东西
<!-- .contact-container is the parent view el -->
<section class="contact-container">
</section>
然后将子视图呈现到容器中:
initialize: function() {
//don't give the views an element, let them render into the detached
//element generated by Backbone
this.view[0] = new CL.Views.ContactView({model: this.model});
this.view[1] = new CL.Views.ContactEdit({model: this.model});
this.currentViewIndex = 0;
},
render: function() {
//replace the contents with the new view
this.view[this.currentViewIndex].render();
this.$el.html(this.view.el);
return this;
},
swap: function() {
var i = this.currentViewIndex;
this.view[i].remove();
//toggle current view between 0 and 1
this.currentViewIndex = i === 0 ? 1: 0;
this.render();
}
然后你得到
<!-- .contact-container is the parent view el -->
<section class="contact-container">
<div><!-- your child element --></div>
</section>
正确理解您的问题,您需要一个继承自基本View
的视图,以便它们可以使用相同的Model
自由操作。
/** Declare base View with specific Model. */
var baseView = Backbone.View.extend({
model: someModel
});
var contactView = baseView.extend({
initialize: function() {
/** Do work specific to contact view. */
}
});
var editContactView = baseView.extend({
initialize: function() {
/** Do work specific to contact edit. */
}
});
var mainView = Backbone.View.extend({
initialize: function() {
/** Initialize two contact views */
new contactView({ });
new editContactView({ });
}
});