回答:
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'
});
}
});
parentEvents = _.result(ParentView.prototype, 'events');
場合events
は、関数かどうかを「手動で」チェックする代わりに使用できるはずです。
_.result
。これは、私が以前に気づかなかったものです。興味を持っている人にとって、ここでこのテーマのバリエーションの束とjsfiddleだ:jsfiddle
this
、インスタンス名で親クラスを呼び出す必要があることです。本当にありがとうございました。
soldier.mothの答えは良いものです。さらに単純化すると、次のようにすることができます
var ChildView = ParentView.extend({
initialize: function(){
_.extend(this.events, ParentView.prototype.events);
}
});
次に、通常の方法でどちらかのクラスでイベントを定義します。
this.events
およびParentView.prototype.events
両方が同じイベントにハンドラを定義するとそうでない親のハンドラは子供を上書きします。
{},ParentView.prototype.events,this.events
delegateEvents
イベントをバインドするためにコンストラクターで呼び出されます。では、それをで拡張すると、initialize
なぜ手遅れにならないのですか?
initialize
ある場合、いくつかのケースで必然的に自分で書くことに気づくでしょう(そして、その機能の階層の管理にも対処する必要があります)。イベントオブジェクトをマージします。events
それ自体の中でマージを維持するために私にはよりクリーンに思えます。そうは言っても、私はこのアプローチについて考えたことはなかったでしょう。別の方法で物事を見るように強いられるのはいつも素晴らしいことです:)
このdefaults
メソッドを使用して、空のオブジェクトの作成を回避することもできます{}
。
var ChildView = ParentView.extend({
events: function(){
return _.defaults({
'click' : 'onclickChild'
}, ParentView.prototype.events);
}
});
階層を上るイベントの継承を処理する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つから拡張するだけで継承できます。
イベント関数をサブビューでプログラムで定義する必要はありません。サブビューでは、ほとんどの場合、親コンストラクターを明示的に参照する必要があります。
これも機能します:
class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'
class ChildView extends ParentView
events: ->
_.extend({}, _.result(_super::, 'events') || {},
'bar' : 'doOtherThing')
ストレートsuper
を使用することは私にとってはうまくいきませんでした。手動で、ParentView
または継承されたクラスを指定していました。
_super
コーヒースクリプト内で利用できる変数へのアクセスClass … extends …
// 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 */ }
});
バックボーンバージョン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",
}
この記事でもっと興味深い解決策を見つけました
バックボーンのスーパーと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と属性についても同じことができます。
この例では、関数によって設定されたプロパティは扱いませんが、記事の作成者はその場合の解決策を提供します。
これを完全に親クラスで行い、子クラスで関数ベースのイベントハッシュをサポートして、子が継承を認識できないようにすることができます(子が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);
};
}
});
ParentView
がイベントをオブジェクトとして定義
していて、イベントを動的に定義する必要がないことが確実な場合ChildView
は、関数を削除して_.extend
直接使用することにより、soldier.mothの回答をさらに簡略化できます。
var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});
var ChildView = ParentView.extend({
events: _.extend({}, ParentView.prototype.events, {
'click' : 'onclickChild'
})
});
私が気に入っているこのパターンは、コンストラクターの変更といくつかの追加機能の追加です。
// 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
。
うわー、ここにはたくさんの答えがありますが、もう1つあげたいと思いました。BackSupportライブラリを使用すると、が提供されますextend2
。使用するextend2
と、自動的にマージevents
(defaults
および同様のプロパティ)が処理されます。
ここに簡単な例があります:
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
extend2
)は私が思いついた最高のextend
方法であり、それほどひどいとは思いません。Backboneに慣れている人は既にに慣れているため、新しいコマンドを覚える必要はありません。