javascript – 使用Backbone事件来切换集合视图属性还是jQuery.siblings()?

想象一下以下简单的模型和视图:

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上介绍了一些博客文章和问题,但没有得出真正令人满意的结论.也许有人可以解释这个具体问题.

最佳答案 编辑:完全错过了messageview上的点击事件,因此修改了我的答案.

只要单击视图,就可以在模型上触发单击事件,然后等待父视图触发模型上的显示和隐藏事件.

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

也许不是实现这种事情的最短途径.但它完全由事件驱动和定制.

点赞