9

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

为了使我的应用程序更具可扩展性,我希望将每个事件ClosedTune委托给TuneBook,但为了可维护性,我希望使用相同的处理程序(Tune存储TuneBook在)。

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

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

4

3 回答 3

11

在您的父视图(也从 扩展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);
    },

}));

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

var ClosedTune = Backbone.View.extend({

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

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

});

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

于 2012-05-07T18:21:38.473 回答
7

这里有几种可能性。

1. 中心化:将ClosedTune对象存储在TuneBook实例中

  1. 存储对每个ClosedTunein的引用tune_book.tunes。你如何填充tune_book.tunes取决于你;既然你提到了一个加法器方法TuneBook,这就是我在下面说明的。

  2. 在事件处理程序中,使用类似事件目标的属性作为键来TuneBook检索ClosedTunefrom 。然后调用or处理程序。tune_book.tunesidTuneClosedTune

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. 去中心化:将视图对象与 DOM 对象关联起来使用jQuery.data()

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

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

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

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);

对评论的回应

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

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

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

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

这两种技术都将ClosedTune对象存储在TuneBook实例中。在@ggozad 的技术中,它只是隐藏在一个抽象背后,这可能会让你不那么明显。

在我的示例中,它们存储在一个普通的 JS 对象 ( tune_book.tunes) 中。在@ggozad 中,它们存储_callbacksBackbone.Events.

添加一个ClosedTune

1.

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

2.

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

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

1.

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

tune.remove();

2.

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

tune.remove();

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

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

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

您存储对ClosedTune对象的引用的一种或另一种方式:使用jQuery.data, 或在 中的普通对象中TuneBook,或_callbacksTuneBook.

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

于 2012-05-07T17:42:14.267 回答
0

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

扩展一个通用主干事件对象并将其存储在参考中vent

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

将其传递给父视图:

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

子视图会触发一个事件:

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

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

父视图正在监听子事件:

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

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

于 2018-12-09T19:49:27.007 回答