9

私のビューTuneBookには、タイプClosedTuneの子ビューがいくつかあります。私はまた、各曲ごとに別々のフルページビュー、OpenTuneを持っています。同じイベントがClosedTuneOpenTuneにバインドされているので、両方とも共有 '抽象的な'ビューTuneから継承するようにアプリケーションを設計しました。バックボーンの親ビューへのイベントの委任

私のアプリは、よりスケーラブルにするために、私はTuneBookに委任される各ClosedTuneのイベントをしたいと思いますが、保守のために、私は(彼らはと思いますが、TuneBookで使用されるように同じハンドラ(Tuneに保存されているもの)を希望明らかにいくつかの関数でラップする必要があります)。

問題は、TuneBookの中で、ハンドラーを呼び出すために正しいClosedTuneを見つけることです。これを設計する良い方法は何ですか、またはイベントを親ビューに委譲するための他の良いソリューションがありますか?

からBackbone View: Inherit and extend events from parentのない複製(私はDOMでの親の子ノードである子供について求めているのに対し、親クラスから継承する子どもたちである)

+0

'' Tune'モデルでハンドラーによってイベントが実行されるのであれば、 '' ChildView - > ParentView - > Model' ..という迂回路を作りたいのであれば、 '' ChildView'から 'Model.handler'を直接実行することができます。 ..もう一方では、 'Model'をメッセンジャーとして使うことができます:ChildViewからモデルの状態を変更し、ParentViewにこの変更を聞かせることができます。 – fguillen

+0

@fguillen親ビューのDOMノードに1回ではなく、各子ビューのDOMノードに、イベントをバインドする方が効率的ではありません(おそらく、バックボーンのカスタムイベントではなくDOMイベントを意味するはずです)。私のアプリのパフォーマンスによって生成された300行のテーブルでは、各行に直接バインドするときには目立って鈍いです。 'ParentView - > [childView] - > childModel' – wheresrhys

答えて

10

親ビュー(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の親ビューと、以前にあなたが使用していた特別なid属性をoptionsに渡しています。

var ClosedTune = Backbone.View.extend({ 

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

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

}); 

あなたはもちろんTuneまたはOpenTuneに類似した加入者を設定することができます。

+2

このアプローチでは、親ビューを「イベントアグリゲータ」として使用します。これは、従うべき良いパターンです。 また、イベントアグリゲータである別のオブジェクトを作成し、それを子ビューに渡すこともできます。次に、子供はイベントを聴くための正しいオブジェクトを見つけるためにDOMをトラバースする必要はありません。 イベントアグリゲータの詳細については、 http://lostechies.com/derickbailey/2012/04/03/revisiting-the-backbone-event-aggregator-lessons-learned/ –

+0

これは完璧に見えます。今夜試してみよう – wheresrhys

+0

私はこれをほぼ正確に実装しました。私は、 'TuneBook'のaddOne()メソッド内に' ClosedTune'のイベントリスナーを添付して、a)親への参照を渡す必要がないようにしましたb)他のコンテキスト内の 'ClosedTune'の内容 – wheresrhys

7

ここにいるカップル可能性の

1.集中:店舗TuneBookインスタンスでClosedTuneオブジェクト

  1. ストアtune_book.tunesClosedTuneを参照。 tune_book.tunesの人口はあなた次第です。 TuneBookの加算メソッドについて述べたので、これを以下に示します。 TuneBookイベントハンドラで

  2. 、キーとしてイベントターゲットのid属性のようなものを使用してtune_book.tunesからClosedTuneを取得します。その後、TuneまたはClosedTuneハンドラを呼び出します。

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.分散:あなたはClosedTuneを作成すると、例えば、それへの参照を格納し、jQuery.data()

  1. を使用してDOMオブジェクトとビューオブジェクトを関連付けますthis.$el.data('view_object', this)

  2. イベントリスナーでは、ClosedTuneを取得します。 $(event.target).data('view_object')

したい場合は、(TuneBookに)ClosedTuneのためにまったく同じハンドラを使用して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にチューンモデルのコレクションを持っていると私は「別のオブジェクトをしたくなかったとして、それに対して決定私はそれはあなたがする必要性を感じ、そしてなぜ、ハウスキーピング/同期の種類によって異なると思い同期

を維持する必要がありますdが。

(例:TuneModel.remove()では、チューンブックのリストからビューを削除する必要があります。イベントを行うにはイベントが必要なので、イベントのみのソリューションがより魅力的になり始めます)。

は、なぜあなたは、「ビューのtunebookのリストからビューを削除する必要がある」と感じています? (私はあなたがするべきではないと示唆しているわけではありません。なぜあなたはが欲しいですか?)あなたはそうしているので、@ ggozadのアプローチはその点でどのように違うと思いますか?

どちらのテクニックも、TuneBookインスタンスにClosedTuneオブジェクトを格納します。 @ ggozadのテクニックでは、抽象化の背後に隠れているので、おそらくそれはあなたにはあまり明らかになりません。

私の例では、それらはプレーンなJSオブジェクト(tune_book.tunes)に格納されています。 @ ggozadでは、Backbone.Eventsによって使用される_callbacks構造体に格納されます。

ClosedTuneの追加:

1.

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

2.

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

あなたはClosedTuneを取り除きたい場合は(あなたがtune.remove()で文書からそれを削除して言いますビューオブジェクトを完全に消したい場合)、@ ggozadの手法を使用すると、ClosedTune iへの孤立した参照が残されますあなたは、私が提案したアプローチで理にかなって、ハウスキーピングの同じ種類を実行しない限り、nはtune_book._callbacks

1.

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

tune.remove(); 

2.

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

tune.remove(); 

各例の最初の行はオプションです - ClosedTuneオブジェクトをクリーンアップするかどうかによって異なります。

オプション2は、私が今やっていることに多少なりとも関係していますが、(他の理由で)モデルをデータ属性としてビューに保存することもできます。場所全体の参照を格納するよりも良い方法になるはずです。

まあ、それは最終的に物事を構成する方法の好みになります。より集中的にビューオブジェクトを格納することをお望みなら、jQuery.dataの代わりにTuneBookインスタンスに格納することができます。 #1:集中を参照してください。 jQuery.dataを使用して、またはTuneBookにプレーンオブジェクトで、またはTuneBook_callbacks中:

一つの方法または別のあなたがClosedTuneオブジェクトへの参照を保存しています。

あなたが理解している理由で@ ggozadのアプローチが好きなら、それを試してみてください。しかしそれは魔法ではありません。ここで紹介しているように、#1で提示したより単純なバージョンと比べて、抽象度のレベルが上がったことによってどのような利点が得られるはずです。

+0

私はオプション1を考えましたが、既にチューンブックのコレクションを持っていて、 sync(TuneModel.remove()など)では、チューンブックのビューのリストからビューを削除する必要があります。これを行うにはイベントが必要なので、イベントのみのソリューションがより魅力的に見えます。オプション2は多かれ少なかれ、私は今やっていることですが、(他の理由で)モデルをデータ属性としてビューに保存します。$ el、より良い方法が必要であると感じるのを助けることはできません場所全体の参照を格納するよりも。 – wheresrhys

+0

@wheresrhysそれがあなたの選択の要因であるかどうかは分かりませんが、以前のコード例での愚かなエラーを修正しました。あなたのコメントへの返答で私の回答を更新しました(「コメントへの応答」を参照)。 – JMM

+0

徹底的な答えに乾杯。イベントを悪影響を与えないように魅力的なものにすることは簡単ですが、オブジェクトを削除するときにコールバックを宣言することをもっと考慮する必要があります。それにもかかわらず、イベントはまだ私の優先オプションです。抽象的なビューへの重複した参照を持つことは、私や他の開発者が誤用するのをより洗練され、魅力的に感じることはありません。 $ .dataの使用に関しては、DOMのやりとりが比較的遅いので絶対に必要でない限り、DOMに照会するものを常に使用することには注意が必要です。 – wheresrhys

関連する問題