Backbone JS:一个视图能否触发其他视图的更新?

45
在我的简单项目中,我有两个视图 - 一个行项目视图(BrandView)和应用程序视图(AppView)。我附加了一些允许选择多个项目的函数:
var BrandView = Backbone.View.extend({
...some code...
    toggle_select: function() {
        this.model.selected = !this.model.selected;
        if(this.model.selected) $(this.el).addClass('selected');
        else $(this.el).removeClass('selected');
        return this;
    }
});
var AppView = Backbone.View.extend({ ...some code... delete_selected: function() { _.each(Brands.selected(), function(model){ model.delete_selected(); }); return false; }, });
问题是,我想知道有多少项被选中。在这种情况下,选择不会影响模型,因此不会触发任何事件。从MVC概念上理解,视图不应直接与其他视图通信。那么AppView如何知道BrandViews中有项目被选择?
更具体地说,我希望AppView知道选择了多少个项目,因此如果选择了多个项目,我会显示一个多选菜单。

2
顺便提一下, $(this.el).toggleClass("selected")。或者更简短地说, this.model.selected = $(this.el).toggleClass('selected").hasClass("selected"); - Mark Rushakoff
7个回答

74

实际上,这正是我最终阅读的内容http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/并开始使用它。好东西。谢谢) - mvbl fst
这真的帮了我很大的忙。谢谢你。 - Bharat
我之前已经读过那篇文章,但是这篇内容的提炼使得它更容易理解。为清晰度加一分! - Richard
非常有用。我最终将其添加到Backbone.View.prototype中,因为我只需要在视图中使用它。:)谢谢! - jasonmerino
太棒了。我一直在寻找如何实施这种方法。 - shrimpwagon
2
在View中,最好使用listenTo而不是on,这样您就不必担心自己清理侦听器。http://backbonejs.org/#Events-listenTo - Lambart

7
我使用Addy Osmani所称的中介者模式http://addyosmani.com/largescalejavascript/#mediatorpattern。整篇文章都值得一读。
基本上,它是一个事件管理器,允许您订阅和发布事件。因此,您的AppView会订阅一个事件,例如“selected”。然后BrandView会发布“selected”事件。
我喜欢这个的原因是它允许您在视图之间发送事件,而不需要将视图直接绑定在一起。
例如:
var mediator = new Mediator(); //LOOK AT THE LINK FOR IMPLEMENTATION

var BrandView = Backbone.View.extend({
    toggle_select: function() {
        ...
        mediator.publish('selected', any, data, you, want);
        return this;
    }
});

var AppView = Backbone.View.extend({
    initialize: function() {
        mediator.subscribe('selected', this.delete_selected)
    },

    delete_selected: function(any, data, you, want) {
        ... do something ...
    },
});

这种方式使你的应用视图无需在乎发布“selected”事件的是BrandView还是FooView,只需关注事件是否发生。因此,我认为这是一种可维护的管理应用程序各部分事件的方式,而不仅仅是视图。
如果你进一步了解“Facade”,你可以创建一个良好的权限结构。这将允许你说只有“AppView”可以订阅我的“selected”事件。我发现这很有帮助,因为它明确了事件的使用位置。

Backbone原生支持这个吗? - mvbl fst
这不是Backbone的一部分。然而,这就是我喜欢它的原因。如果你直接将一个视图中的DOM事件绑定到另一个视图,当你尝试更改视图时,会变得非常混乱。我会更新我的答案来尝试澄清我的意思。 - John McKim

1
这里有一个与之类似的需求案例:Backbone的listenTo看起来是解决超时或未经身份验证的请求重定向到登录页面的解决方案。
我将事件处理程序添加到我的路由器中,并使其监听全局事件,例如:
Backbone.Router.extend({
    onNotAuthenticated:function(errMsg){
        var redirectView = new LoginView();
        redirectView.displayMessage(errMsg);
        this.loadView(redirectView);
    },
    initialize:function(){
        this.listenTo(Backbone,'auth:not-authenticated',this.onNotAuthenticated);  
    },
    .....
});

在我的 jQuery AJAX 错误处理程序中:
$(document).ajaxError(
    function(event, jqxhr, settings, thrownError){
        .......
        if(httpErrorHeaderValue==="some-value"){
             Backbone.trigger("auth:not-authenticated",errMsg);
        }
    });     

1

忽略你在帖子中已经提到的问题,你可以将事件绑定和触发到全局的Backbone.Event对象上,这将允许任何东西与其他任何东西进行通信。这绝不是最好的解决方案,如果你的视图之间相互聊天,那么你应该考虑重构。但是,希望这可以帮助你。


1
你可以将Backbone对象用作事件总线。
这种方法相对更加清晰,但仍然依赖于全局的Backbone对象。
var view1 = Backbone.View.extend({

  _onEvent : function(){
    Backbone.trigger('customEvent');
  }

});


var view2 = Backbone.View.extend({

  initialize : function(){
    Backbone.on('customEvent', this._onCustomEvent, this);
  },

  _onCustomEvent : function(){
    // react to document edit.
  }

});

0
与John所建议的一样,在这种情况下中介者模式非常有用,正如Addy Osmani在 Backbone Fundamentals中再次总结的那样。
最终我选择了 Backbone.Mediator插件,它非常简单而且优秀,使得我的AMD视图模块能够无缝协同工作 =)

0

使用相同的模型对象。AppView 可以使用集合进行初始化,而 BrandView 可以使用该集合中的一个模型进行初始化。当分支对象的属性发生更改时,任何具有对该模型引用的其他代码都可以读取它。

因此,假设您通过集合获取了一些品牌:

var brands = new Brands([]);
brands.fetch();

现在您可以创建一个AppView,以及为每个型号创建一个BrandView数组。
var appView = new AppView({brands: brands});
var brandViews = brands.map(function(brand) {
  return new BrandView({brand: brand});
});

现在,appView和brandViews都可以访问相同的模型对象,因此当您更改其中一个时:

brands.get(0).selected = true;

然后,当被引用的视图访问它时,它也会发生变化。
console.log(appView.brands.get(0).selected); // true
console.log(brandViews[0].brand.selected)    // true

你的前两个代码示例已经被使用了。我知道如何获取所选品牌的数量。问题是,一个视图如何知道另一个视图中的某些内容是否被选中/取消选中。 - mvbl fst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接