[解決済み] Backbone.jsでサブビューの初期化およびレンダリングを処理するには?
質問
ビューとそのサブビューを初期化してレンダリングする方法が3種類あり、それぞれ異なる問題があります。すべての問題を解決する、より良い方法があるかどうか知りたいのです。
シナリオ1
親のinitialize関数の中で子供を初期化する。こうすることで、すべてがレンダリングに滞留することがなくなり、レンダリング時のブロックが少なくなります。
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.render().appendTo(this.$('.container-placeholder');
}
問題点
-
最大の問題は、親で 2 回目に render を呼び出すと、子側のイベント バインディングがすべて削除されることです。(これは、jQueryの
$.html()
が動作します)。これを緩和するにはthis.child.delegateEvents().render().appendTo(this.$el);
代わりに、最初の、そして最もよくあるケースは、不必要に多くの作業をすることになります。 -
子要素を追加することで、レンダー関数に親の DOM 構造に関する知識を持たせ、希望する順序になるようにします。つまり、テンプレートを変更すると、ビューのレンダー関数を更新する必要がある場合があります。
シナリオ2
子を初期化するのは、親の
initialize()
のままですが、追記する代わりに
setElement().delegateEvents()
を使って、親テンプレートの要素に子要素を設定します。
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}
問題点
-
これによって
delegateEvents()
これは、最初のシナリオではその後の呼び出しで必要であったのに対して、少しマイナスです。
シナリオ3
子を初期化するのは、親の
render()
メソッドに置き換えます。
initialize : function () {
//parent init stuff
},
render : function () {
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
問題点
-
これは、レンダー関数が、すべての初期化ロジックと同様に結びつけられる必要があることを意味します。
-
子ビューの 1 つの状態を編集してから親ビューの render を呼び出すと、まったく新しい子が作成され、その現在の状態はすべて失われます。また、メモリリークのために危険な状態になる可能性もあるようです。
この件に関して、皆さんのご意見を伺いたいと思います。それとも、これらの問題をすべて解決する、4つ目の魔法のようなシナリオがあるのでしょうか?
Viewのレンダリング状態を記録したことはありますか?例えば
renderedBefore
フラグ?実に稚拙な感じがします。
解決方法は?
これはいい質問ですね。Backboneは仮定がないので素晴らしいのですが、このようなことを自分で(どのように)実装するかを決めなければならないことを意味します。自分自身のものに目を通した結果、私はシナリオ1とシナリオ2を(なんとなく)混ぜて使っていることがわかりました。シナリオ1とシナリオ2が混在しているのは、単純に考えて、シナリオ1とシナリオ2でやることはすべてやらなければならないので、4つ目の魔法のシナリオは存在しないと思います。
私がどのように処理したいかは、例で説明するのが一番わかりやすいと思います。例えば、このシンプルなページが、指定されたビューに分割されているとします。
レンダリング後のHTMLは、次のようなものだとする。
<div id="parent">
<div id="name">Person: Kevin Peel</div>
<div id="info">
First name: <span class="first_name">Kevin</span><br />
Last name: <span class="last_name">Peel</span><br />
</div>
<div>Phone Numbers:</div>
<div id="phone_numbers">
<div>#1: 123-456-7890</div>
<div>#2: 456-789-0123</div>
</div>
</div>
HTMLがどのように図と一致しているか、一目瞭然であることを望みます。
は
ParentView
は2つの子ビューを保持しています。
InfoView
と
PhoneListView
と同様に、いくつかの余分なdiv、そのうちの1つ。
#name
を設定する必要があります。
PhoneListView
の配列で、それ自身の子ビューを保持しています。
PhoneView
のエントリーがあります。
では、実際の質問に移ります。 私は、ビューの種類によって、初期化とレンダリングを異なる方法で処理しています。 私はビューを2つのタイプに分割しています。
Parent
ビューと
Child
ビューになります。
両者の違いは簡単です。
Parent
は子ビューを保持するのに対し
Child
ビューにはありません。ですから、私の例では
ParentView
と
PhoneListView
は
Parent
を表示するのに対し
InfoView
と
PhoneView
のエントリは
Child
のビューを表示します。
前にも述べたように、この2つのカテゴリーの最大の違いは、レンダリングが許可されるタイミングです。完璧な世界では、私は
Parent
ビューは一度だけレンダリングされます。モデルが変更された場合の再レンダリングは、その子ビューに任されます。
Child
一方、ビューは、他のビューに依存しないので、必要なときにいつでも再レンダリングできるようにしています。
もう少し詳しく説明すると
Parent
ビューが好きです。
initialize
関数は、いくつかのことを行うことができます。
- 自作ビューの初期化
- 自作ビューのレンダリング
- 任意の子ビューを作成し、初期化する。
-
各子ビューに、私のビュー内の要素を割り当てます (例.
InfoView
には#info
).
ステップ1は、かなり自己説明的です。
ステップ2のレンダリングでは、子ビューが依存する要素がすでに存在していることを確認してから、それを割り当てようとします。こうすることで、すべての子ビューの
events
は正しく設定され、再委任の心配をせずに何度でもブロックを再レンダリングすることができます。私は、実際には
render
子ビューは、各自の
initialization
.
ステップ3と4は、実は同時に処理されます。
el
を子ビューの作成時に入力します。ここで要素を渡すのが好きなのは、親が自身のビューのどこに子コンテンツを置くことができるかを決めるべきだと思っているからです。
レンダリングについては、なるべくシンプルに
Parent
ビューになります。私は
render
関数は、親ビューをレンダリングすること以外は何もしません。イベントデリゲーションも、子ビューのレンダリングも、何もしません。ただ単純にレンダリングするだけです。
しかし、これがいつもうまくいくとは限りません。例えば、上の例では
#name
要素は、モデル内の名前が変わるたびに更新される必要があります。しかし、このブロックは
ParentView
テンプレートで処理されることはなく、専用の
Child
を表示させることができるので、それを回避しています。私は、ある種の
subRender
という関数があります。
のみ
の内容を置き換えます。
#name
要素全体をゴミ箱に入れる必要はありません。
#parent
要素を使用します。これはハックに見えるかもしれませんが、DOM全体を再レンダリングして要素を付け直したりすることを心配するよりも、この方法が効果的だと実感しています。もし本当にきれいにしたいのであれば、私は新しい
Child
ビューを表示します。
InfoView
を処理するようなものです。
#name
ブロックを作成します。
では、次に
Child
ビューでは
initialization
とはかなり似ています。
Parent
ビューを作成しないだけで、さらに
Child
ビューになります。 だから
- ビューを初期化する
- 気になるモデルの変更をリッスンするバインドをセットアップする
- ビューをレンダリングする
Child
ビューのレンダリングも非常にシンプルです。
el
. ここでも、デリゲーションなどをいじる必要はありません。
以下は、私の
ParentView
のように見えるかもしれません。
var ParentView = Backbone.View.extend({
el: "#parent",
initialize: function() {
// Step 1, (init) I want to know anytime the name changes
this.model.bind("change:first_name", this.subRender, this);
this.model.bind("change:last_name", this.subRender, this);
// Step 2, render my own view
this.render();
// Step 3/4, create the children and assign elements
this.infoView = new InfoView({el: "#info", model: this.model});
this.phoneListView = new PhoneListView({el: "#phone_numbers", model: this.model});
},
render: function() {
// Render my template
this.$el.html(this.template());
// Render the name
this.subRender();
},
subRender: function() {
// Set our name block and only our name block
$("#name").html("Person: " + this.model.first_name + " " + this.model.last_name);
}
});
の実装を見ることができます。
subRender
をご覧ください。変更点を
subRender
ではなく
render
そのため、ブロック全体を分解して再構築する心配はありません。
以下は
InfoView
ブロックを作成します。
var InfoView = Backbone.View.extend({
initialize: function() {
// I want to re-render on changes
this.model.bind("change", this.render, this);
// Render
this.render();
},
render: function() {
// Just render my template
this.$el.html(this.template());
}
});
ここで重要なのはバインドです。 モデルにバインドすることで、手動で
render
を自分で作る。 モデルが変更された場合、このブロックは他のビューに影響を与えることなく、自分自身を再レンダリングします。
は
PhoneListView
と似たようなものになります。
ParentView
の両方でもう少しロジックが必要になります。
initialization
と
render
関数を使用して、コレクションを処理することができます。コレクションをどのように扱うかはあなた次第ですが、少なくともコレクションイベントをリッスンして、どのようにレンダリングするか(追加/削除、またはブロック全体を再レンダリング)を決定する必要があります。私自身は、ビュー全体を再レンダリングするのではなく、新しいビューを追加し、古いビューを削除するのが好きです。
は、その
PhoneView
とほぼ同じになります。
InfoView
しかし、気になるモデルの変更だけをリスニングします。
少しでも参考になれば幸いです。わかりにくいところや、説明が不十分なところがあれば教えてください。
関連
-
vue3レスポンシブ対応のためのsetup+ref+reactive
-
vue ディレクティブ v-html と v-text
-
vueが定義するプライベートフィルタと基本的な使い方
-
[解決済み] テスト
-
[解決済み】SyntaxError: JSONの位置1に予期しないトークンoがある。
-
[解決済み】「X-Frame-Options」を「SAMEORIGIN」に設定したため、フレームでの表示を拒否された。
-
[解決済み] Node.jsを完全にアンインストールして、最初から再インストールする方法 (Mac OS X)
-
[解決済み] Node.jsのmodule.exportsの目的と使い方を教えてください。
-
[解決済み] レンダリング後に入力フィールドにフォーカスを設定するには?
-
[解決済み】JavaScriptで2つの配列を結合し、項目の重複を排除する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
vue3レスポンシブ対応のためのsetup+ref+reactive
-
Vueがechartsのtooltipにクリックイベントを追加するケーススタディ
-
vueネットワークリクエストソリューション ネイティブネットワークリクエストとjsネットワークリクエストライブラリ
-
vueにおけるfilterの適用シーンについて解説します。
-
Vueのイベント処理とイベントモディファイアの解説
-
[解決済み】リソースの読み込みに失敗した:Bind関数でサーバーが500(Internal Server Error)のステータスで応答した【非公開
-
[解決済み】JavaScript TypeError: null のプロパティ 'style' を読み取ることができない
-
[解決済み】JavaScriptでインラインIF文の書き方は?
-
[解決済み】エラー。Ionic使用中にモジュール '../lib/utils/unsupported.js' が見つかりませんでした。
-
jq は html ページとデータを動的に分割する。