No.8 イベントモデル
Sencha フレームワーク内部では、イベントが効果的に使われています。 GridのようなコンポーネントにStoreをバインドするだけで、Storeのデータに変更があれば表示が更新されますが、これは内部でイベントのハンドリングをしているからです。
プログラマ自身がイベントを定義して、それを発火させることもできます。 非常に重要な概念ですので、是非おぼえてください。
Sencha フレームワークにおけるイベントには、DOMイベント、Extイベント、カスタムイベントがあります。 最初に、DOMイベントについて見てみましょう。
DOMイベント
ブラウザのDOMでは、DOMノードで発火される様々なイベントが定義されています。 下記はよく利用されるDOMイベントの一部です。
- mousedown
- mouseover
- click
- select
- blur
- focus
- change
Senchaフレームワークの内部では、これらのDOMイベントはExt.EventObjects (Sencha Touch の場合は Ext.event.Event)でラップされることになります。 そのため、ネイティブのイベントを扱うより、簡単に操作でき、クロスブラウザ対応のイベント処理APIを提供してくれます。
全てのDOMイベントは共通の「メソッドシグネチャ」で発火されます。
- event : Ext.EventObject (Ext.event.Event)
- node : HtmlElement
イベントリスナーに渡される最初の引数は、イベントオブジェクトです。 二つ目の引数には、実際にイベントを発火したエレメントが渡されます。 ここで渡されるのはHtmlのエレメントオブジェクトで、ExtのElementオブジェクトではありません。
1 2 3 4 5 | var el = Ext.get('someelement'); el.on('click', function(event, node) { el.highlight(); }); |
Ext イベント
Ext イベントとは、Sencha フレームワークが発火させるイベントのこと指します。 また開発者が独自に定義して利用するイベントのことをカスタムイベントと言います。 カスタムイベントについては次の項で触れますが、 どちらも、Ext.util.Observableクラスの機能を利用しています。
Sencha フレームワークにおける「イベント」は「Observable」デザインパターンに従っています。 「Observable」オブジェクトは任意の数の「Observer」オブジェクトに対して、イベントが発生したときに何が起きたのかを通知することができます。
「Observable」オブジェクトとは、イベントの発火やリスニングをすることができるオブジェクトのことで、具体的にはExt.util.Observable (Sencha Touch では Ext.mixin.Observable クラス) をミックスインしたオブジェクトです。
どんなイベントがあるの?
Sencha フレームワークのコンポーネントは全てが「Observable」です。 コンポーネントはユーザーの操作などによってイベントを発火します。
API ドキュメントには、それぞれのクラスのコンフィグ/プロパティ/メソッドに加えて、そのクラスのオブジェクトが発火するイベントも記載されています。例えば、Sencha Ext JS の Ext.panel.Panel クラスには、45種類のイベントがあります。
コンポーネントのイベントには、ユーザーが何か操作をしたときに発火する、click や change などのイベントがあります。 また、コンポーネントの描画 ( render, beforerender など) に関するものや、状態の変化 (focus, blur, show, hide) に関するものもあります。
これらのイベントをリッスンして、その時の処理を書いていきます。
また、次回に説明するデータモデルで触れる、Storeもイベントを発火します。 Store では、データがロードされたとき (load) 、削除 (remove) された時は、更新 (update) された時などにイベントが発火します。
カスタムイベント
Ext.util.Observableをmixinsコンフィグに追加することにより「Observable」なクラスを作成することができます。 そうすると任意のクラスにおいて容易にカスタムイベントの作成と発火が可能になります。 Ext.util.Observable が組み込まれているクラスのサブクラスも「Observable」です。
これらのクラスではカスタムイベントの定義と「発火」が可能になります。 自分自身の fireEvent メソッドを使ってイベントをコード内で発火させます。 キャンセル可能なカスタムイベントも定義することが可能です。
例:
1 2 3 4 5 6 7 8 9 10 | initComponent: function(){ this.addEvents('beforeexpand', 'expand'); }, expand: function(){ if(this.fireEvent('beforeexpand', this, e) !== false){ // expand 処理 this.fireEvent('expand', this, e); } } |
これは、beforeexpand というイベントを発火し、そのリスナーが false を返した場合は、expand 処理を実行しないという処理の書き方です。
イベントのリッスン
イベントをリッスンするには、DOMイベントの場合も、Extイベントの場合も同じで、 addListenerメソッド(別名 on )を使います。
DOMイベントの場合は、リスナーを呼び出すときのシグネチャは決まっていますが、 Extイベントの場合は、それぞれのイベントによって異なります。 リスナーのシグネチャについては、APIドキュメントを参照して調べます。 次はパネルの resize イベントにリスナーをセットする例です。
1 2 3 4 | var panel = Ext.create('Ext.panel.Panel'); panel.on('resize', function(p, width, height, oldWidth, oldHeight) { // Do something }); |
リスナーはクラスの定義時や、クラスをインスタンス化する時に、listeners コンフィグを設定する方法でもセットできます。
1 2 3 4 5 6 7 | Ext.create('Ext.panel.Panel', { listeners: { 'resize': function(p, width, height, oldWidth, oldHeight) { // Do something } } }) |
また、MVCアーキテクチャーにおいては、コントローラーがイベントのリスニングをします。 この点については、のちほど稿を分けて説明しましょう。
イベントのリスナーはいくつもセットできます。 また、removeListener (別名un) メソッドで、セットしてあるリスナーを削除することもできます。
イベントリスニングの委譲
DOMのイベントを拾う場合、イベントが発生するエレメント全てに対してイベントリスナーをつけるのは避けましょう。 その代わりに、イベントが発生するエレメントのコンテナのエレメントに対してイベントリスナーを一つだけセットして、その中で条件分岐を使って処理を行った方が良い場合があります。
以下のDOM構造があった場合に:
1 2 3 4 5 | <div id="colors"> <div id="red" class="primary">Red</div> <div id="green" class="secondary">Green</div> <div id="blue" class="primary">Blue</div> </div> |
それぞれの色のDIVタグのクリックを処理する簡単な方法としては以下の方法が思いつきます。
1 2 3 | Ext.fly('red').on('click', function(){ /*...*/ }); Ext.fly('green').on('click', function(){ /*...*/ }); Ext.fly('blue').on('click', function(){ /*...*/ }); |
イベントハンドリングを委譲することにより、次のようにすることもできます。
1 2 3 4 5 6 7 | Ext.fly('colors').on('click', function(e,t){ switch(t.id){ case 'red': // ... case 'green': // ... case 'blue': //... } }); |
イベントリスナーの第2引数には、リスナーをセットしたコンテナーのエレメントではなくて、実際にクリックされたエレメントが渡ってきます。 ですから、このように一つのハンドラーで処理できるわけですね。
capture と releaseCapture
Sencha Ext JS の Ext.util.Observable クラスの場合には、capture と releaseCapture という二つの静的メソッドがあります。
1 2 3 | Ext.util.Observable.capture(obj, function() { console.log(arguments); }); |
こうすると obj で発生したすべてのイベントにおいて、第2引数で設定した関数が呼び出されるようになります。 イベントのキャプチャーを止めるには、releaseCaptureメソッドを使います。
1 | Ext.util.Observable.releaseCapture(); |
プログラムのデバッグの時や、あるコンポーネントがどんなイベントをどんな順番で発火するのかを調べたりするときに便利です。 自分が作ったコンポーネントのイベントがちゃんと期待通りに発火しているかのテストにも使えますね。
suspendEvents と resumeEvents
この二つのメソッドで、イベントの発火をコントロールすることができます。 suspendEvents を使うとイベントの発生を一時的に止めることができます。 再開するには、resumeEventsを使います。 suspendEvents の引数に true を渡すと、停止中のイベントはキューに保存され、 resumeEvents が呼び出された時にたまっていたイベントが発火します。 false が渡されるか省略されると、停止中に発火したイベントは破棄されます。
いかがでしたか? Sencha フレームワークのイベント、理解していただけましたでしょうか。 次回は、データモデルについてです。お楽しみに。