想象一下以下简单的模型和视图:
app.Messages = Backbone.Model.extend({
defaults: function() {
return {
message: "an empty message…",
messageActive: false
};
}
});
app.MessageView = Backbone.View.extend({
tagName: "li",
template: _.template($("#template").html()),
events: {
"click": "open"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "message:hide", this.hide);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
open: function() {},
hide: function() {
this.model.set("messageActive", false);
this.$el.removeClass("show-message");
}
});
模型是集合的一部分,所有内容都通过父视图(此问题的简化)保存在一起:
app.AppView = Backbone.View.extend({
el: "#messages",
initialize: function() {
this.listenTo(app.messages, "message:show", this.foo);
},
foo: function(model) {
var open = app.messages.where({messageActive: true});
open.forEach(function(e, i) {
if (model != e) {
e.trigger("message:hide");
}
});
},
在任何给定时间,只有一个集合的消息可以处于活动状态。这意味着只要单击关闭的消息,就会关闭所有打开的消息。现在的问题涉及app.MessageView.open()
的功能。它可以使用jQuery.siblings()
来隐藏其所有兄弟姐妹的信息并展示自己的信息:
open: function() {
this.$el
.addClass("show-message")
.siblings(".show-message")
.removeClass("show-message");
}
我个人认为这个解决方案非常优雅,我只是不喜欢它需要知道它有兄弟姐妹。我的其他实现将使用由父视图(app.AppView.foo()
)拦截的自定义事件,然后在每条打开的消息上调用app.MessageView.close()
:
open: function() {
this.model.set("messageActive", true).trigger("message:show", this.model);
this.$el.addClass("show-message");
}
这对我来说感觉更加“骨干”,但似乎有点过度设计。 ; - )
我可以想到第三种可能性,其中父视图只是跟踪最后打开的模型本身,并触发模型视图截获的任何关闭事件(看起来可管理但对我来说不像骨干;-) )。我已经在SO上介绍了一些博客文章和问题,但没有得出真正令人满意的结论。也许有人可以解释这个具体问题。
答案 0 :(得分:1)
编辑:完全错过了留言视图上的点击事件,因此修改了我的回答。
只要单击视图,就可以在模型上触发单击事件,然后等待父视图触发模型上的显示和隐藏事件。
app.MessageView = Backbone.View.extend({
tagName: "li",
template: _.template($("#template").html()),
events: {
"click": "isClicked"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "message:show", this.open);
this.listenTo(this.model, "message:hide", this.hide);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
isClicked: function(){
this.model.trigger("message:clicked");
},
open: function() {
this.model.set("messageActive", true);
this.$el.addClass("show-message");
},
hide: function() {
this.model.set("messageActive", false);
this.$el.removeClass("show-message");
}
});
您的父视图现在负责在点击其中一个子视图时将正确的事件发送到模型。
app.AppView = Backbone.View.extend({
el: "#messages",
initialize: function() {
this.listenTo(app.messages, "message:clicked", this.foo);
},
foo: function(model) {
var open = app.messages.where({messageActive: true});
open.forEach(function(e, i) {
if (model != e) {
e.trigger("message:hide");
}
});
// Because we retrieved the currently active models before sending this event,
// there will be no racing condition:
model.trigger("message:show");
},
也许不是实现这种事情的最短途径。但它完全由事件驱动和定制。
答案 1 :(得分:1)
作为单挑,我最终得到了接受答案的轻微变化。现在,视图不是触发自定义事件,而是监听模型messageActive
属性的更改并相应地执行操作。
app.MessageView = Backbone.View.extend({
tagName: "li",
template: _.template($("#template").html()),
events: {
"click": "isClicked"
},
initialize: function() {
this.listenTo(this.model, "change:messageActive", this.showMessage);
},
render: function() {
this.$el.html(this.template(this.model.toJSON())).addClass("sprite");
return this;
},
isClicked: function() {
this.model.set("messageActive", true);
},
showMessage: function(model, value) {
this.$el.toggleClass("show-message", value);
}
});
app.AppView = Backbone.View.extend({
el: "#messages",
initialize: function() {
this.listenTo(app.messages, "change:messageActive", this.hideMessages);
},
hideMessages: function(model, value) {
if (value) {
var open = app.messages.where({messageActive: true});
open.forEach(function(e, i) {
if (model != e) {
e.set("messageActive", false);
}
});
}
},