バックボーンビュー:親からのイベントの継承と拡張


115

バックボーンのドキュメントには、

イベントプロパティは、イベントハッシュを返す関数として定義することもできます。これにより、プログラムでイベントを簡単に定義したり、親ビューから継承したりできます。

親のビューイベントをどのように継承して拡張しますか?

親ビュー

var ParentView = Backbone.View.extend({
   events: {
      'click': 'onclick'
   }
});

子ビュー

var ChildView = ParentView.extend({
   events: function(){
      ????
   }
});

回答:


189

1つの方法は次のとおりです。

var ChildView = ParentView.extend({
   events: function(){
      return _.extend({},ParentView.prototype.events,{
          'click' : 'onclickChild'
      });
   }
});

もう一つは:

var ParentView = Backbone.View.extend({
   originalEvents: {
      'click': 'onclick'
   },
   //Override this event hash in
   //a child view
   additionalEvents: {
   },
   events : function() {
      return _.extend({},this.originalEvents,this.additionalEvents);
   }
});

var ChildView = ParentView.extend({
   additionalEvents: {
      'click' : ' onclickChild'
   }
});

イベントが関数かオブジェクトかを確認するには

var ChildView = ParentView.extend({
   events: function(){
      var parentEvents = ParentView.prototype.events;
      if(_.isFunction(parentEvents)){
          parentEvents = parentEvents();
      }
      return _.extend({},parentEvents,{
          'click' : 'onclickChild'
      });
   }
});

それは素晴らしい...多分これを更新して、ChildViewから継承する方法を示すことができます(プロトタイプイベントが関数またはオブジェクトであるかどうかを確認してください)...または、私はこの継承全体について考えすぎています。
ブレント

@brent Sure、3つ目のケースが追加されました
soldier.moth

14
私が誤っていないparentEvents = _.result(ParentView.prototype, 'events');場合eventsは、関数かどうかを「手動で」チェックする代わりに使用できるはずです。
公園。

3
@公園。アンダースコアユーティリティ関数について言及したことに対する+1 _.result。これは、私が以前に気づかなかったものです。興味を持っている人にとって、ここでこのテーマのバリエーションの束とjsfiddleだ:jsfiddle
EleventyOne

1
ここに私の2セントを投入するために、私は2番目のオプションが最良の解決策だと思います。私がこれを言っているのは、それが本当にカプセル化されている唯一の方法であるという純粋な事実のためです。使用される唯一のコンテキストはthis、インスタンス名で親クラスを呼び出す必要があることです。本当にありがとうございました。
ジェシージェームズジャクソンテイラー2014年

79

soldier.mothの答えは良いものです。さらに単純化すると、次のようにすることができます

var ChildView = ParentView.extend({
   initialize: function(){
       _.extend(this.events, ParentView.prototype.events);
   }
});

次に、通常の方法でどちらかのクラスでイベントを定義します。


8
あなたはおそらくスワップにしたいのに良いの呼び出し、this.eventsおよびParentView.prototype.events両方が同じイベントにハンドラを定義するとそうでない親のハンドラは子供を上書きします。
soldier.moth 2012

1
@ Soldier.moth、それを次のように編集しました{},ParentView.prototype.events,this.events
AJP

1
明らかにこれは機能しますが、私が知っているように、delegateEventsイベントをバインドするためにコンストラクターで呼び出されます。では、それをで拡張すると、initializeなぜ手遅れにならないのですか?
SelimOber

2
それは非常に難しいですが、このソリューションの私の問題は次のとおりです:ビューの多様で豊富な階層がinitializeある場合、いくつかのケースで必然的に自分で書くことに気づくでしょう(そして、その機能の階層の管理にも対処する必要があります)。イベントオブジェクトをマージします。eventsそれ自体の中でマージを維持するために私にはよりクリーンに思えます。そうは言っても、私はこのアプローチについて考えたことはなかったでしょう。別の方法で物事を見るように強いられるのはいつも素晴らしいことです:)
EleventyOne

1
初期化の前にdelegateEventsが呼び出されるため、この回答は無効になりました(バージョン1.2.3の場合はこれに該当します)-注釈付きソースでこれを行うのは簡単です。
Roey、2015年

12

このdefaultsメソッドを使用して、空のオブジェクトの作成を回避することもできます{}

var ChildView = ParentView.extend({
  events: function(){
    return _.defaults({
      'click' : 'onclickChild'
    }, ParentView.prototype.events);
  }
});

2
これにより、親ハンドラーが子ハンドラーの後にバインドされます。ほとんどの場合問題はありませんが、子イベントが親イベントをキャンセル(オーバーライドではなく)する必要がある場合、それは不可能です。
公園。

10

CoffeeScriptを使用して関数をeventsに設定すると、を使用できますsuper

class ParentView extends Backbone.View
  events: ->
    'foo' : 'doSomething'

class ChildView extends ParentView
  events: ->
    _.extend {}, super,
      'bar' : 'doOtherThing'

これは、親イベント変数がオブジェクトではなく関数である場合にのみ機能します。
Michael

6

階層を上るイベントの継承を処理するBackbone.Viewから特殊な基本コンストラクターを作成する方が簡単ではないでしょうか。

BaseView = Backbone.View.extend {
    # your prototype defaults
},
{
    # redefine the 'extend' function as decorated function of Backbone.View
    extend: (protoProps, staticProps) ->
      parent = this

      # we have access to the parent constructor as 'this' so we don't need
      # to mess around with the instance context when dealing with solutions
      # where the constructor has already been created - we won't need to
      # make calls with the likes of the following:   
      #    this.constructor.__super__.events
      inheritedEvents = _.extend {}, 
                        (parent.prototype.events ?= {}),
                        (protoProps.events ?= {})

      protoProps.events = inheritedEvents
      view = Backbone.View.extend.apply parent, arguments

      return view
}

これにより、再定義された拡張関数を使用して新しい「サブクラス」(子コンストラクタ)を作成するたびに、イベントハッシュを削減(マージ)できます。

# AppView is a child constructor created by the redefined extend function
# found in BaseView.extend.
AppView = BaseView.extend {
    events: {
        'click #app-main': 'clickAppMain'
    }
}

# SectionView, in turn inherits from AppView, and will have a reduced/merged
# events hash. AppView.prototype.events = {'click #app-main': ...., 'click #section-main': ... }
SectionView = AppView.extend {
    events: {
        'click #section-main': 'clickSectionMain'
    }
}

# instantiated views still keep the prototype chain, nothing has changed
# sectionView instanceof SectionView => true 
# sectionView instanceof AppView => true
# sectionView instanceof BaseView => true
# sectionView instanceof Backbone.View => also true, redefining 'extend' does not break the prototype chain. 
sectionView = new SectionView { 
    el: ....
    model: ....
} 

特別なビューを作成することにより、extend関数を再定義するBaseView、親ビューの宣言されたイベントを継承したいサブビュー(AppView、SectionViewなど)をBaseViewまたはその派生物の1つから拡張するだけで継承できます。

イベント関数をサブビューでプログラムで定義する必要はありません。サブビューでは、ほとんどの場合、親コンストラクターを明示的に参照する必要があります。


2

@ soldier.mothの最後の提案の短いバージョン:

var ChildView = ParentView.extend({
  events: function(){
    return _.extend({}, _.result(ParentView.prototype, 'events') || {}, {
      'click' : 'onclickChild'
    });
  }
});

2

これも機能します:

class ParentView extends Backbone.View
  events: ->
    'foo' : 'doSomething'

class ChildView extends ParentView
  events: ->
    _.extend({}, _.result(_super::, 'events') || {},
      'bar' : 'doOtherThing')

ストレートsuperを使用することは私にとってはうまくいきませんでした。手動で、ParentViewまたは継承されたクラスを指定していました。

_superコーヒースクリプト内で利用できる変数へのアクセスClass … extends …


2

// ModalView.js
var ModalView = Backbone.View.extend({
	events: {
		'click .close-button': 'closeButtonClicked'
	},
	closeButtonClicked: function() { /* Whatever */ }
	// Other stuff that the modal does
});

ModalView.extend = function(child) {
	var view = Backbone.View.extend.apply(this, arguments);
	view.prototype.events = _.extend({}, this.prototype.events, child.events);
	return view;
};

// MessageModalView.js
var MessageModalView = ModalView.extend({
	events: {
		'click .share': 'shareButtonClicked'
	},
	shareButtonClicked: function() { /* Whatever */ }
});

// ChatModalView.js
var ChatModalView = ModalView.extend({
	events: {
		'click .send-button': 'sendButtonClicked'
	},
	sendButtonClicked: function() { /* Whatever */ }
});

http://danhough.com/blog/backbone-view-inheritance/


1

バックボーンバージョン1.2.3の場合、__super__正常に動作し、連鎖することもあります。例えば:

// A_View.js
var a_view = B_View.extend({
    // ...
    events: function(){
        return _.extend({}, a_view.__super__.events.call(this), { // Function - call it
            "click .a_foo": "a_bar",
        });
    }
    // ...
});

// B_View.js
var b_view = C_View.extend({
    // ...
    events: function(){
        return _.extend({}, b_view.__super__.events, { // Object refence
            "click .b_foo": "b_bar",
        });
    }
    // ...
});

// C_View.js
var c_view = Backbone.View.extend({
    // ...
    events: {
        "click .c_foo": "c_bar",
    }
    // ...
});

...--- A_View.js結果は次のようになります:

events: {
    "click .a_foo": "a_bar",
    "click .b_foo": "b_bar",
    "click .c_foo": "c_bar",
}

1

この記事でもっと興味深い解決策を見つけました

バックボーンのスーパーとECMAScriptのhasOwnPropertyを使用しています。プログレッシブの2番目の例は、魅力のように機能します。ここに少しコードがあります:

var ModalView = Backbone.View.extend({
    constructor: function() {
        var prototype = this.constructor.prototype;

        this.events = {};
        this.defaultOptions = {};
        this.className = "";

        while (prototype) {
            if (prototype.hasOwnProperty("events")) {
                _.defaults(this.events, prototype.events);
            }
            if (prototype.hasOwnProperty("defaultOptions")) {
                _.defaults(this.defaultOptions, prototype.defaultOptions);
            }
            if (prototype.hasOwnProperty("className")) {
                this.className += " " + prototype.className;
            }
            prototype = prototype.constructor.__super__;
        }

        Backbone.View.apply(this, arguments);
    },
    ...
});

UI属性についても同じことができます

この例では、関数によって設定されたプロパティは扱いませんが、記事の作成者はその場合の解決策を提供します。


1

これを完全に親クラスで行い、子クラスで関数ベースのイベントハッシュをサポートして、子が継承を認識できないようにすることができます(子がMyView.prototype.initializeオーバーライドする場合は子を呼び出す必要がありますinitialize)。

var MyView = Backbone.View.extend({
  events: { /* ... */ },

  initialize: function(settings)
  {
    var origChildEvents = this.events;
    this.events = function() {
      var childEvents = origChildEvents;
      if(_.isFunction(childEvents))
         childEvents = childEvents.call(this);
      return _.extend({}, MyView.prototype.events, childEvents);
    };
  }
});

0

このCoffeeScriptソリューションは私にとってうまくいきました(そして@ soldier.mothの提案を考慮に入れています):

class ParentView extends Backbone.View
  events: ->
    'foo' : 'doSomething'

class ChildView extends ParentView
  events: ->
    _.extend({}, _.result(ParentView.prototype, 'events') || {},
      'bar' : 'doOtherThing')

0

ParentViewがイベントをオブジェクトとして定義 していて、イベントを動的に定義する必要がないことが確実な場合ChildViewは、関数を削除して_.extend直接使用することにより、soldier.mothの回答をさらに簡略化できます。

var ParentView = Backbone.View.extend({
    events: {
        'click': 'onclick'
    }
});

var ChildView = ParentView.extend({
    events: _.extend({}, ParentView.prototype.events, {
        'click' : 'onclickChild'
    })
});

0

私が気に入っているこのパターンは、コンストラクターの変更といくつかの追加機能の追加です。

// App View
var AppView = Backbone.View.extend({

    constructor: function(){
        this.events = _.result(this, 'events', {});
        Backbone.View.apply(this, arguments);
    },

    _superEvents: function(events){
        var sooper = _.result(this.constructor.__super__, 'events', {});
        return _.extend({}, sooper, events);
    }

});

// Parent View
var ParentView = AppView.extend({

    events: {
        'click': 'onclick'
    }

});

// Child View
var ChildView = ParentView.extend({

    events: function(){
        return this._superEvents({
            'click' : 'onclickChild'
        });
    }

});

親を特定する必要がないため、この方法をお勧めします。変更する変数が1つ少なくなります。私はのために同じロジックを使用attributesしてdefaults


0

うわー、ここにはたくさんの答えがありますが、もう1つあげたいと思いました。BackSupportライブラリを使用すると、が提供されますextend2。使用するextend2と、自動的にマージeventsdefaultsおよび同様のプロパティ)が処理されます。

ここに簡単な例があります:

var Parent = BackSupport.View.extend({
    events: {
        change: '_handleChange'
    }
});
var Child = parent.extend2({
    events: {
        click: '_handleClick'
    }
});
Child.prototype.events.change // exists
Child.prototype.events.click // exists

https://github.com/machineghost/BackSupport


3
コンセプトは好きですが、原則として、「extend2」が適切な関数名であると考えるライブラリを渡します。
Yaniv 2016年

基本的に「Backbone.extendですが、機能が改善された」関数の名前を付けるための提案をお待ちしています。Extend 2.0(extend2)は私が思いついた最高のextend方法であり、それほどひどいとは思いません。Backboneに慣れている人は既にに慣れているため、新しいコマンドを覚える必要はありません。
machineghost 2016年

Githubリポジトリで問題を開きました。:)
Yaniv 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.