SubView的なモノのより良い管理方法 (Backbone Advent Calendar 2012 24th day)
Posted: Updated:
前段
- Backbone.js (Sub)View Rendering Trick | Joe Zim's JavaScript Blog
- Rendering Views in Backbone.js Isn't Always Simple by Ian Storm Taylor
- Break Apart Your Backbone.js Render Methods by Ian Storm Taylor
海外のイケメンたちが書いた記事からくみ取ったパターンを、ひっじょーに薄めて紹介します。SubViewの中身までは及ばず、単純にMainViewが所有する要素の中で、SubViewをrenderするときの簡単な定義について。
MainViewの中にSubViewを設ける
MainView(ページ全体を司るView)の中に、SubView(部分的なView)を埋め込むときのパターンについて。より具体的には、初日に紹介した構成で言うところの、PageViewとPartialViewに該当しますが、ここでは単純にMain/Subとします。
原始的パターン
おおよそ原始的には、このようなパターンになります。
/** * @class MainView */ var MainView = Backbone.View.extend({ render: function() { this.$el.html(this.template()); this.subview = new SubView(); this.$el.find('#subview-container').append(this.subview.render().$el); return this; } });
MainViewをrenderして、SubViewが入るべき要素が生成されたときにSubViewを初期化して、所定の位置にぶっ込むというスタンス。まあまあ、素直なのですが、いくらか効率的でない点があります。
- MainViewがリフレッシュされるたびにSubViewがinitializeされるオーバーヘッド
- もしかしたら解放されないSubViewの中のイベントリスナ
- renderメソッドの中が分厚くなって肥大化する恐れ
Badとは言いませんが、まあまあ微妙な感じなので、ちょっと洗練させてみましょう
現実的パターン
役割を少し分離させて、View本体の初期化が1度になるようにします。
/** * @class MainView */ var MainView = Backbone.View.extend({ initialize: function() { this.subview = new SubView(); }, render: function () { this.$el.html(this.template()); this.subview.setElement(this.$('#subview')).render(); return this; } });
よいかんじです。newする(initializeが発生する)のは1度で済むようになりました。ViewのsetElement
メソッドのおかげで、これは当該Viewが管理する要素を再設定するメソッドです。このメソッドはそれなりに賢く、元の要素に対してはundelegateEvents
をして、新しい要素には改めてdelegateEvents
をします。
要素を付け替える度に、イベントリスナをリセットしてくれる感じです
抽象化
これを抽象化して細かいロジックを隠蔽すると、このようになります。
/** * @class MainView */ var MainView = Backbone.View.extend({ initialize: function() { this.subview = new SubView(); }, render: function() { this.$el.html(this.template()); this.assign(this.subview, '#subview') return this; }, assign: function(view, selector) { view.setElement(this.$(selector)).render(); } });
こんな感じですね。assign
メソッドによってSubViewの要素を再設定するあたりがまとめられています。次のようなパターンも考えられます。
/** * @class BaseView */ var BaseView = Backbone.View.extend({ assignedViews: {}, render: function() { var that = this; this.$el.html(this.presenter()); Object.keys(this.assignedViews).forEach(function(selector) { that.assignedViews[selector].setElement(that.$el.find(selector)); }); return this; }, assign: function(selector, view) { this.assignedViews[selector] = view; } }); /** * @class MainView * @extends BaseView */ var MainView = BaseView.extend({ initialize: function() { this.subview = new SubView(); this.assign('#subview', this.subview); }, presenter: function() { return this.template(); } });
処理を親クラスのメソッドに追い出していくと、個別に書く処理をさらに薄くできます。前回紹介した、render
とpresenter
の分離を併用しています。renderするときに、assignしておいたSubViewをまとめてsetElementしてrenderしています。
そんな感じ
えー、そんな感じで1日遅れの24日目のBackbone.js Advent Calendar 2012でございました。
MainViewからSubViewを操作するのに、イベントを介すのは面倒こともあるので、二度手間のように見えても、MainViewのプロパティとしてSubViewを提供し、その上でassign(setElement)したほうがよろしい感じです。参照距離は近いほうが良いですね。
内包要素でなく、まったく異なる要素(Action的なモノとか)を扱う場合はまた違うのですが、Partial的に扱うSubViewであれば、こんな感じをベースとして管理できます。
さて、25日目どうしよう...:;(∩´﹏`∩);: