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日目どうしよう...:;(∩´﹏`∩);: