Modern ツールキットでのデータバインディング – Sencha ブログより

近頃、Sencha 本家のブログでは技術資料的な記事が少し少なくなっていましたが、先日久々に技術的な記事が掲載されました。

ご存じのように、Ext JS 6 では、以前の Ext JS (Version 5まで) と、Sencha Touch という2つのフレームワークがマージされました。 これまで、Ext JS のユーザーが Ext JS 6 へ移行する記事は、いくつか見られたのですが、Sencha Touch ユーザーに向けた、Ext JS 6 の解説記事はありませんでした。

この記事では、Ext JS 6 の ViewModel を modern ツールキットで使う方法が書かれています。ツールキットとは、Ext JS 6 のビジュアルな部分を受け持つところで、現在は、classic と modern の2つのツールキットが存在します。 modern ツールキットは、Sencha Touch の流れをくむツールキットです。

ViewModel は、Ext JS 5 で導入された、MVVM あるいは M-V-VM-VC という新しいアーキテクチャで採用された、ビューに関連してデータを保持するクラスです。 Sencha Touch だけをやってきた方には馴染みのない概念だと思いますが、これを利用することで、非常にアプリケーションを作成しやすくなります。

ではご覧ください。 Data Binding in the Ext JS Modern Toolkit | Sencha の翻訳です。

はじめに

Ext JS 6 で “ツールキット” というコンセプトが導入されました。 これは、(主とする)フレームワークのビジュアルコンポーネントを指定するものです。 以前の Ext JS でのアプリケーションのビルドは、バージョン6では、”classic” ツールキットを使ったものにアップグレードし、Sencha Touch アプリケーションは、”modern” ツールキットが使われます。 双方のアプリケーションファミリーにとっての大きな利点は、共通した core パッケージが使えることです。 これは、ユニバーサルアプリケーションを構築する開発者が使えるというだけでなく、Sencha Touch アプリケーションを modern ツールキットにアップグレードする際に、多くの新機能 (特に重要なのは ViewModel とデータバインディング) が使えるようになります。

これらの機能をまだ見たことがない方は、 View Models and Data Binding Guide をご覧になると良いでしょう。 これにより、データバインディングという概念に関する強固な土台ができることでしょう。 この記事では、modern ツールキットにおけるデータバインディングの働きに商店を宛、いくつかの便利なテクニックを紹介しましょう。

ViewModel

Ext.app.ViewModel くらすは、データバインディングを可能にする要となります。 ViewModel は、Ext.data.Model と似ていますが、data をフィールドのフラットなセットとして扱うのではなく、オブジェクトとプロパティの階層として扱い、階層内の変更を監視する為の bind() メソッドが提供されます。

ViewModel の階層

ViewModel の階層は、JavaScript のプロトタイプチェーンのように動作します。 子コンポーネントのViewModel は、親の ViewModel の中に含まれる全ての情報にアクセスできます。 孫やひ孫が、親子の関係を遡って、上位レベルのコンポーネントデータにアクセスできます。

次のサンプルでは、子アイテムが親の ViewModel にアクセスできることを示します。 また、 チェーンの中にプロパティを追加したり、非破壊的にそれらを変更することができます。 バインディングは、常にチェーンを上に向かって検索し、 値が見つかったらそれが返されます。

この Fiddle をご覧ください

上記のコードは、次の出力を返します。

The Don is Vito Corleone but I also know Genco Abbandando\ The new Don is Sonny Corleone\ Wait he’s not dead! Long live Vito Corleone!

ViewModel のライフサイクル

modern ツールキットでは、ViewModel の階層は、コンポーネント生成の間に確立されます。 これは単純ですが、ViewModel の親は、あとで変更できない関係であるということを理解するために不可欠な概念です。 この理由で、子要素を生成する際には、インスタンスを生成して手動で追加するのではなく、コンフィグを使うことを推奨します。

例えば、ユーザーがExt.create でボタンを生成する場合、ボタンは、ViewModel の親のチェーンなしに生成されます。 このボタンがあとでコンテナーに追加されても、チェーンが再接続されることはありません。

バインディング

データバインディングは、Ext.Widget (Ext.Component の基底クラス) の機能で、ViewModel の変更を監視します。 変更はその後、コンポーネントのコンフィグに、コンフィグのセッターメソッドによって渡されます。 ViewModel の値が変更されたときには、その値にバインドされたすべてのコンフィグのセッターメソッドが新しい値で呼び出されます。

Two-Way Binding

すべてのバインディングは、Ext JS や Sencha Touch の開発者にはおなじみの、標準のセッターやアップデータのワークフローを通して動作します。 単方向のバインディングでは、コンフィグのセッターは、ViewModel の値が変更される度に呼び出されます。 これは、双方向バインディングでも、アップデータがインターセプトされる以外は同様です。 コンフィグの値が変更されアップデータに渡されたとき、 ViewModel のバインドされた値も変更されます。 更新する他のすべてのバインド値を順番に変更します。

コンフィグで双方向バインディングを定義する際には、便利な twoWayBindable コンフィグを使います。 普通は、このコンフィグは内部的に使われますが、 合致するコンフィグプロパティを持つカスタムビューやコンポーネントを書く際に便利です。 例えば、Ext.field.Text の定義では、value コンフィグを次のように双方向バインディングとして定義しています。

[js] twoWayBindable: { value: 1 } [/js]

次のサンプルでは、スピナーフィールドでの双方向バインディングで、簡単に formulas と連携できることを示します。

この Fiddle をご覧ください

スピナーフィールドの値が変更されると、 そのアップデータは、ViewModel にフックされます。 このトリガーで、下のコンテナーのバインディングがhtml コンフィグのセッターを呼び出します。 これは Ext.field.Spinner が、value コンフィグへの変更をを通知し、双方向バインディングが可能に設定されている Ext.field.Text を基底クラスとしているから、動作するのです。

双方向バインディングを無効にする

To disable the two-way binding in the above example, we could replace this piece of code: 上記のサンプルで双方向バインディングを無効にするには、 一部のコードを修正します。

[js] bind: ‘{people}’ [/js]

上記の部分を次のように変更します。

[js] bind: { value: { bindTo: ‘{people}’, twoWay: false // a bind option } } [/js]

上記のコードはバインディングのいくつかの側面を示しています。 最初のケースでは、(スピナーの “value” である) defaultBindProperty に頼りました。 bind が単なる文字列のとき、bind オプションは、デフォルト値にセットされます。カスタムの bind オプションを指定するには、明示的に bindTo プロパティを使い、追加の属性を指定してバインディングを指定します。

このサンプルで双方向バインディングを無効にすると、スピナーを変更しても、コンテナーの html は変更されなくなることがわかるでしょう。

発行 (Publishing)

他のコンポーネントから利用したいコンポーネントのコンフィグは、ViewModel に発行することができます。 publishes コンフィグは、twoWayBindable と同様に、アップデータをフックしてコンフィグの値を ViewModel にプッシュします。 バインディングと違って、発行される値には、ViewModel で変更されたときに呼び出されるセッターがありません。

より重要なことに、データはViewModel 内に置かれるのではなく、発行されたコンフィグの値は、コンポーネントの名前を参照して名付けられたオブジェクトの中に保存されます。 多くのコンポーネントには、デフォルトの publishes コンフィグがあり、単純に、コンポーネントに reference コンフィグを設定することによってこれらの値は、ViewModel からアクセスできます。

list を使う時に、他のコンポーネントがリストの中のどのアイテムが選択されているかを知りたいような時にこれは、特に便利です。

次のサンプルでは、Ext.dataview.DataView には selection プロパティを発行するExt.mixin.Selectableがミックスインされていることを使います。 これはフレームワークに組み込まれているので、Ext.dataview.List のインスタンスは、選択されたアイテムに他のコンポーネントがアクセスできます。

この Fiddle をご覧ください

Forms and Fields

modern ツールキットでは、フォームのフィールドは、最初からデータバインディングが動作するように設定されています。 Ext.field.Text を継承したすべてのコンポーネントは、value を ViewModel に発行し、双方向バインディングするように設定されています。 チェックボックスでは、checked コンフィグが発効され、これもまた双方向バインディングと連結されています。 スライダーも、双方向バインディングが有効で、単一または複数の値が発行されます。

次のサンプルでは、複数のフォーム要素を接続します。 セレクトフィールドでフォームの情報をフィルタさせ、チェックボックスでイメージのクラスをコントロールします。 Ext.field.Checkbox は、checked プロパティを発行するので、 そこに接続するためには、'{checkboxReferenceName.checked}' を使います。

この Fiddle をご覧ください

上記のサンプルでは、 1つの値を ViewModel の値に直接バインドすることで、 いくつかの視覚的結果を得ることができることがわかると思います。 例えば、次の行を

[js] userCls: ‘{friend.checked:pick("", "friend")}’, [/js]

このように変更してみましょう。

[js] userCls: ‘{person.value.friend:pick("", "friend")}’, [/js]

リスト

Ext.dataview.List など Ext.data.DataView を継承したクラスを使う時には、 データバインディングは、itemConfig プロパティを使ってアイテム事に有効にできます。 レコードデータは、このコンフィグの中で bind ステートメントで record オブジェクトを使うことで、利用可能です。

例えば、”propertyName” というプロパティを参照するには、'{record.propertyName}' にバインドします。 欠くアイテムは、自身の ViewModel を生成して、その中に自身の formulas やバインドできるプロパティを生成できます。

次のサンプルでは、アイテムの ViewModel にカスタムの formula を使って、 計算を実行しています。 また、リストのストア内のレコードにバインドして、 各リストアイテムのコンポーネントの内側のテンプレートを設定しています。 最後に、ストアのデータをセレクトフィールドにバインドしています。

この Fiddle をご覧ください

グリッド

グリッドのデータバインディングは、非常にパワフルで各セルがレコードデータにアクセスできます。 グリッドは、親クラスの Ext.dataview.List のように、グリッドの itemConfig 内の viewModel コンフィグの設定で、行の中の全てのセルにレコードをバインドできます。

例えば、 Ext.grid.cell.Widget をセル内で使う時には、そのコンポーネントにレコードデータをバインドできます。 他によくあるグリッドのバインディングの例としては、セルの innerCls に接続することもあります。 そうすると、レコードからの値の組み合わせを元にして、セルのスタイルを設定できます。

次のサンプルでは、 双方の使用例を示します。 ボタンコンポーネントをグリッドセルの中に生成して、pick フォーマッタで条件による書式を設定しています。

この Fiddle をご覧ください

まとめ

ViewModel とデータバインディングのパワーは、modern ツールキットで本領を発揮します。 Config システムに依存しているおかげで、ほとんどのコンフィグプロパティは、バインドリクエストの応答します。 通常の用途でアプリケーションを素早く稼働できるようにするために、共通のコンポーネントは、最初から twoWayBindable とコンフィグの発行を利用できます。 データバインディングは、アプリケーションを構築する方法を基本的に変えます、そして、テキストフィールドから大きなグリッドまですべてはそれらに影響を及ぼすことができます。 好きなようにアプリケーションを書くために、データバインディングがあなたの開発者としてよく使うツールになると思います。