javascript 将事件委托给 Backbone 中的父视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10485610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 10:03:09  来源:igfitidea点击:

Delegating events to a parent view in Backbone

javascriptmodel-view-controllereventsbackbone.jsdelegation

提问by wheresrhys

My view, TuneBook, has several child views of type ClosedTune. I also have separate full page views for each tune, OpenTune. The same events are bound within ClosedTuneand OpenTune, so I've designed my app so that they both inherit from a shared 'abstract' view Tune.

我的视图 ,TuneBook有几个类型为 的子视图ClosedTune。对于每首曲子,我也有单独的完整页面视图OpenTuneClosedTune和中绑定了相同的事件OpenTune,因此我设计了我的应用程序,以便它们都继承自共享的“抽象”视图Tune

To make my app more scaleable I would like the events for each ClosedTuneto be delegated to TuneBook, but for maintainability I would like the same handlers (the ones stored in Tune) to be used by TuneBook(although they'd obviously need to be wrapped in some function).

为了使我的应用程序更具可扩展性,我希望将每个事件的事件ClosedTune委托给TuneBook,但为了可维护性,我希望使用相同的处理程序(存储在 中的处理程序TuneTuneBook(尽管它们显然需要包含在某些函数中)。

The problem I have is, within TuneBook, finding the correct ClosedTuneto call the handler on. What's a good way to architect this, or are there other good solutions for delegating events to a parent view?

我遇到的问题是,在 内TuneBook找到ClosedTune调用处理程序的正确方法。什么是构建此架构的好方法,或者是否有其他好的解决方案将事件委托给父视图?

Note- not a duplicate of Backbone View: Inherit and extend events from parent(which is about children inheriting from a parent class, whereas I'm asking about children which are child nodes of the parent in the DOM)

注意- 不是Backbone View: Inherit and extend events from parent(这是关于从父类继承的子类,而我询问的是 DOM 中父类的子节点的子节点)

回答by ggozad

In your parent view (extending also from Backbone.Events), I would bind onEventto the DOM event. On trigger, it would fire a backbone event including some "id" attribute that your child views know (presumably some row id?).

在您的父视图中(也从 扩展Backbone.Events),我将绑定onEvent到 DOM 事件。触发时,它会触发一个主干事件,包括您的子视图知道的一些“id”属性(大概是某个行 ID?)。

var TuneBook = Backbone.View.extend(_.extend({}, Backbone.Events, {
    events: {
        "click .tune .button": "clickHandler"
    },
    clickHandler: function (ev) {
        this.trigger('buttonClick:' + ev.some_id_attr, ev);
    },

}));

Child views would then naturally subscribe to the parent views event that concerns them. Below I do it in initializepassing the parent view as well as that special id attribute you used before in options.

然后子视图自然会订阅与它们相关的父视图事件。下面我initialize通过传递父视图以及您之前在options.

var ClosedTune = Backbone.View.extend({

    initialize: function (options) {
        options.parent.on('buttonClick:' + options.id, this.handler, this);
    },

    handler: function (ev) {
        ...
    },

});

You can of course also set up similar subscribers on Tuneor OpenTune.

您当然也可以在Tune或上设置类似的订阅者OpenTune

回答by JMM

Here are a couple of possibilities.

这里有几种可能性。

1. Centralized: store ClosedTuneobjects in the TuneBookinstance

1.集中式:将ClosedTune对象存储在TuneBook实例中

  1. Store a reference to each ClosedTunein tune_book.tunes. How you populate tune_book.tunesis up to you; since you mentioned an adder method on TuneBook, that's what I've illustrated below.

  2. In the TuneBookevent handler, retrieve the ClosedTunefrom tune_book.tunesby using something like the idattribute of the event target as the key. Then call the Tuneor ClosedTunehandler.

  1. 店里每一个参考ClosedTunetune_book.tunes。您如何填充tune_book.tunes取决于您;既然您提到了 上的加法器方法TuneBook,这就是我在下面说明的内容。

  2. TuneBook事件处理程序中,通过使用诸如事件目标的属性之类的东西作为键来检索ClosedTunefrom 。然后调用or处理程序。tune_book.tunesidTuneClosedTune

http://jsfiddle.net/p5QMT/1/

http://jsfiddle.net/p5QMT/1/

var Tune = Backbone.View.extend({
  className: "tune",

  click_handler: function (event) {
    event.preventDefault();
    console.log(this.id + " clicked");
  },

  render: function () {
    this.$el.html(
      '<a href="" class="button">' + this.id + '</a>'
    );

    return this;
  }
});

var ClosedTune = Tune.extend({});

var OpenTune = Tune.extend({
  events: {
    "click .button" : 'click_handler'
  }
});

var TuneBook = Backbone.View.extend({
  events: {
    "click .tune .button" : 'click_handler'
  },

  click_handler: function (event) {
    var tune = this.options.tunes[
      $(event.target).closest(".tune").attr('id')
    ];

    tune.click_handler( event );
  },

  add_tune: function (tune) {
    this.options.tunes[tune.id] = tune;
    this.$el.append(tune.render().el);
  },

  render: function () {
    $("body").append(this.el);
    return this;
  }
});

var tune_book = new TuneBook({
  tunes: {}
});

[1, 2, 3].forEach(function (number) {
  tune_book.add_tune(new ClosedTune({
    id: "closed-tune-" + number
  }));
});

tune_book.render();

var open_tune = new OpenTune({
  id: "open-tune-1"
});

$("body").append(open_tune.render().el);

2. Decentralized: associate the view object with the DOM object using jQuery.data()

2. 去中心化:将视图对象与 DOM 对象关联使用 jQuery.data()

  1. When you create a ClosedTune, store a reference to it, e.g. this.$el.data('view_object', this).

  2. In the event listener, retrieve the ClosedTune, e.g. $(event.target).data('view_object').

  1. 创建 时ClosedTune,请存储对它的引用,例如this.$el.data('view_object', this).

  2. 在事件侦听器中,检索ClosedTune,例如$(event.target).data('view_object')

You can use the same exact handler for ClosedTune(in TuneBook) and OpenTune, if you want.

如果需要,您可以对ClosedTune(in TuneBook) 和使用完全相同的处理程序OpenTune

http://jsfiddle.net/jQZNF/1/

http://jsfiddle.net/jQZNF/1/

var Tune = Backbone.View.extend({
  className: "tune",

  initialize: function (options) {
    this.$el.data('view_object', this);
  },

  click_handler: function (event) {
    event.preventDefault();

    var tune =
      $(event.target).closest(".tune").data('view_object');

    console.log(tune.id + " clicked");
  },

  render: function () {
    this.$el.html(
      '<a href="" class="button">' + this.id + '</a>'
    );

    return this;
  }
});

var ClosedTune = Tune.extend({
  initialize: function (options) {
    this.constructor.__super__.initialize.call(this, options);
  }
});

var OpenTune = Tune.extend({
  events: {
    "click .button" : 'click_handler'
  }
});

var TuneBook = Backbone.View.extend({
  events: {
    "click .tune .button": Tune.prototype.click_handler
  },

  add_tune: function (tune) {
    this.$el.append(tune.render().el);
  },

  render: function () {
    $("body").append(this.el);
    return this;
  }
});

var tune_book = new TuneBook({
  tunes: {}
});

[1, 2, 3].forEach(function (number) {
  tune_book.add_tune(new ClosedTune({
    id: "closed-tune-" + number
  }));
});

tune_book.render();

var open_tune = new OpenTune({
  id: "open-tune-1"
});

$("body").append(open_tune.render().el);

Response to comment

回复评论

I considered option 1 but decided against it as I already have a collection of tune models in the tunebook and didn't want another object I'd need to keep in sync

我考虑了选项 1,但决定反对它,因为我已经在 tunebook 中收集了一组调谐模型并且不想要另一个我需要保持同步的对象

I guess it depends what kind of housekeeping / syncing you feel the need to do, and why.

我想这取决于您觉得需要做什么样的内务管理/同步,以及为什么。

(e.g. in TuneModel.remove() I would need to remove the view from tunebook's list of views... would probably need events to do this, so an event only solution starts to look more attractive).

(例如,在 TuneModel.remove() 中,我需要从 tunebook 的视图列表中删除视图......可能需要事件来执行此操作,因此仅事件解决方案开始看起来更具吸引力)。

Whydo you feel that you "need to remove the view from tunebook's list of views"? (I'm not suggesting you shouldn't, just asking why youwant to.) Since you do, how do you think @ggozad's approach differs in that respect?

为什么您觉得您“需要从 tunebook 的视图列表中删除该视图”?(我并不是建议您不应该这样做,只是问为什么要这样做。)既然您这样做了,那么您认为 @ggozad 的方法在这方面有何不同?

Both techniques store ClosedTuneobjects in the TuneBookinstance. In @ggozad's technique it's just hidden behind an abstraction that perhaps makes it less obvious to you.

这两种技术都ClosedTuneTuneBook实例中存储对象。在@ggozad 的技术中,它只是隐藏在一个抽象之后,这可能会使您不那么明显。

In my example they're stored in a plain JS object (tune_book.tunes). In @ggozad's they're stored in the _callbacksstructure used by Backbone.Events.

在我的示例中,它们存储在一个普通的 JS 对象 ( tune_book.tunes) 中。在@ ggozad就是他们存储在_callbacks所用的结构Backbone.Events

Adding a ClosedTune:

添加一个ClosedTune

1.

1.

this.options.tunes[tune.id] = tune;

2.

2.

this.on('buttonClick:' + tune.id, tune.handler, tune);

If you want to get rid of a ClosedTune(say you remove it from the document with tune.remove()and you want the view object gone completely), using @ggozad's approach will leave an orphaned reference to the ClosedTunein tune_book._callbacksunless you perform the same kind of housekeeping that would make sense with the approach I suggested:

如果你想摆脱一个ClosedTune(假设你从文档中删除它tune.remove()并且你希望视图对象完全消失),使用@ggozad 的方法将留下对ClosedTunein的孤立引用,tune_book._callbacks除非你执行相同类型的内务处理我建议的方法的意义:

1.

1.

delete this.options.tunes[tune.id];

tune.remove();

2.

2.

this.off("buttonClick:" + tune.id);

tune.remove();

The first line of each example is optional -- depending if you want to clean up the ClosedTuneobjects or not.

每个示例的第一行都是可选的——取决于您是否想要清理ClosedTune对象。

Option 2 is more or less what I'm doing right now, but (for other reasons) I also store the model as a data attribute on view.$el, and I can't help feeling that there's got to be a better way than storing references all over the place.

选项 2 或多或少是我现在正在做的,但是(出于其他原因)我还将模型作为数据属性存储在 view.$el 上,我不禁觉得必须有更好的方法而不是到处存储引用。

Well, it ultimately comes down to your preference for how to structure things. If you prefer storing the view objects in a more centralized fashion, you can store them in the TuneBookinstance instead of using jQuery.data. See #1: Centralized.

好吧,这最终归结为您对如何构建事物的偏好。如果您更喜欢以更集中的方式存储视图对象,则可以将它们存储在TuneBook实例中而不是使用jQuery.data. 参见 #1:集中式。

One way or another you're storing references to the ClosedTuneobjects: using jQuery.data, or in a plain object in the TuneBook, or in _callbacksin the TuneBook.

拉上你存储引用的ClosedTune对象:使用jQuery.data,或在一个普通的对象TuneBook,或_callbacksTuneBook

If you like @ggozad's approach for reasons that you understand, go for it, but it's not magic. As it's presented here I'm not sure what advantage is supposed to be provided by the extra level of abstraction compared to the more straightforward version I present in #1. If there is some advantage, feel free to fill me in.

如果您出于您理解的原因喜欢@ggozad 的方法,那就去做吧,但这并不神奇。正如这里介绍的那样,与我在 #1 中介绍的更直接的版本相比,我不确定额外的抽象级别应该提供什么优势。如果有什么优势,请随时填写我。

回答by Shahar Shokrani

Great solution I have taken from this article(@dave-cadwallader comment).

我从这篇文章中得到了很好的解决方案(@dave-cadwallader 评论)。

Extend an general backbone events object and store it in a reference vent:

扩展通用主干事件对象并将其存储在引用中vent

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

Pass it to parent view:

将其传递给父视图:

var parentView = new ParentView({vent: vent});

The child view will trigger an event:

子视图会触发一个事件:

ChildView = Backbone.View.extend({    
  initialize: function(options){
    this.vent = options.vent;
  },

  myHandler: function(){
    this.vent.trigger("myEvent", this.model);
  }
});

And the parent view is listening to the child event:

并且父视图正在侦听子事件:

ParentView = Backbone.View.extend({    
    initialize: function(options){
        this.vent = options.vent;
        this.vent.on("myEvent", this.onMyEvent);

        let childView = new ChildView({vent: this.vent});
    },

    onMyEvent: function(){
        console.log("Child event has been ");
    }
});

Disclaimer - pay attention that the ventobject has to be injected to every view so you will find in this article better design patterns to make use of.

免责声明 - 请注意,必须将vent对象注入到每个视图中,因此您会在本文中找到更好的设计模式来使用。