Backbone JS:一个视图可以触发其他视图的更新

Backbone JS: can one view trigger updates in other views?

本文关键字:视图 其他 更新 一个 JS Backbone      更新时间:2023-09-26

在我的简单项目中,我有两个视图-行项目视图(品牌)和应用程序。我有附加的功能,允许选择多个项目:

var BrandView=Backbone.View.extend({…一些代码。。。toggle_select:函数(){this.model.selected=!this.model.selected;if(this.model.selected)$(this.el).addClass('selected');else$(this.el).removeClass(‘selected’);返回此;}});var AppView=Backbone.View.extend({…一些代码。。。delete_selected:function(){_.each(Brands.selected(),function(model){model.delete_selected();});return false;},});

问题是,我想知道有多少项目被选中。在此设置中,选择不会影响模型,因此不会引发任何事件。从MVC概念中,我了解到视图不应该直接与其他视图对话。那么,AppView如何知道BrandViews中正在选择某些内容呢?

更具体地说,我AppView可以知道选择了多少项,所以如果选择了1项以上,我会显示一个多选菜单。

您可能想了解一下关于Backbone pub/sub事件的讨论:

http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/

我喜欢将其添加为全局事件机制:

Backbone.pubSub = _.extend({}, Backbone.Events);

然后在一个视图中,您可以触发一个事件:

Backbone.pubSub.trigger('my-event', payload);

在另一个你可以听到:

Backbone.pubSub.on('my-event', this.onMyEvent, this);

我使用Addy Osmani所称的中介模式http://addyosmani.com/largescalejavascript/#mediatorpattern.整篇文章很值得一读。

基本上,它是一个事件管理器,允许您订阅和发布事件。因此,您的AppView会为一个事件添加下标,即"selected"。然后BrandView将发布"选定"事件。

我喜欢它的原因是它允许您在视图之间发送事件,而无需将视图直接绑定在一起。

例如

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

这样,你的应用程序视图就不在乎发布"选定"事件的是BrandView还是FooView,只在乎事件发生了。因此,我发现它是一种可维护的方式来管理应用程序各部分之间的事件,而不仅仅是视图。

如果你进一步阅读"Facade",你可以创建一个很好的权限结构。这将允许您说只有"AppView"可以订阅我的"选定"事件。我觉得这很有帮助,因为它非常清楚地表明了事件的使用位置。

忽略您在文章中已经提到的问题,您可以将事件绑定到全局Backbone.Event对象并从中触发事件,这将允许任何东西与其他任何东西对话。这绝对不是最好的解决方案,如果你有彼此聊天的观点,那么你应该考虑重构它。但你来了!希望这能有所帮助。

这是我有类似需求的案例: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);
        }
    });     

您可以使用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.
  }
});

使用相同的模型对象。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

正如John在上面所建议的,中介模式在这种情况下非常有效,正如Addy Osmani在Backbone基础知识中再次总结的那样。

最后使用Backbone.Mediator插件,它简单而伟大,使我的AMD View模块无缝地协同工作=)