第三回ExtJS勉強会の資料です。
えー、プロジェクタで映したとき用のURLです。
http://code.xenophy.com/
今回は、イベント制御について説明します。登場する主な機能は「イベントリスナー」と「イベントディスパッチ」です。
イベント制御は、ExtJSをはじめ、非同期で動作するアプリケーション作成には、いまでは欠かせない存在ではないでしょうか。
イベントリスナー
イベントリスナーとは、何かイベントが起きたときに反応し動作するメソッドやファンクションです。
ExtJSでいえば、「パネルのレンダリングが終わった。」「ツリーノードのドラッグが開始されたなどです。」
イベントリスナーの登録は、登録したいオブジェクトのaddListenerを利用して登録を行います。または、登録したいオブジェクトを生成するときに、コンフィグオプションでlistenersで登録することも可能です。
イベントディスパッチ
イベントリスナーは、イベントが発生したときに反応するハンドラです。イベントディスパッチとは、逆にイベントを発生させることをいいます。
ExtJSでは、「発火」とよばれているようで、fireEventがそれに当たります。
このメソッド、fireEventはExt.util.Observableで実装されています。ExtJS内のほとんどのクラスが、このExt.util.Observableクラスを継承しているため、ExtJSを使っているいたるところで、このfireEventが利用可能です。
イベントリスナーとイベントディスパッチの関係
イベント定義
既に、ExtJSで定義されているイベントに関しては、APIドキュメントに書いてあるイベント名を第一引数にいれて、発火することでイベントを発生させることができます。
しかし、自分自身でオリジナルのイベント名を定義する場合、addEventsメソッドを利用してイベントを先に定義する必要があります。
オリジナルのイベントを定義無しにいきなりfireEventすると、JavaScriptエラーになりますので、ご注意を。
イベント定義の例
this.addEvents(
‘windowcloseall’
);
コラム
イベント定義は、どこで行うのか?についてです。Ext.Panelを継承しているクラスであれば、initEventsメソッドをオーバーライドして、そこで定義したほうがよいかな?というのが個人的な見解です。もし、Ext.Panelを継承していないクラスの場合で、Ext.Componentを継承しているのでれば、initComponentメソッドをオーバーライドして定義、もうExt.util.Obaservableしか継承してないよーってときは、コンストラクタを定義して、そこで。
余談ですが、それぞれのタイミングで、initEventsだけするならどこでもいいでしょう・・・だけど、コンポーネント系になってくるとエレメント自体が生成し終わっているかどうかも関わってくる場合があるので、その場合は注意が必要です。
(第四回とか・・でクラス継承についてもういっちょ細かく説明しようかしら、最近新しい形に変えたので・・継承方法を・・)
具体的な使用例
では、イベント制御はどんなときに使うのでしょうか?下記の例で考えてみてください。
- ボタンがあります。押下するとウィンドウが表示されます。
- もう一つボタンがあります。押下すると現在表示されているすべてのウィンドウを閉じます。
さて、こんなときに使ってみると便利です。
な・ぜ・か?
表示さているウィンドウの個数がn個だからです
僕は昔、イベントリスナー意味わかんねーいらねーっていう派だったんですが、実際使い始めてから、ないと困るようになりましたねぇ・・・。適材適所で使っていきましょう。便利なんで・・・。
確かに、生成したウィンドウのインスタンスを配列やオブジェクトに保持して、閉じるボタンが押されたときに、ループで次々閉じていけば、イベントリスナーを使わなくても実現できそうです。
しかし、そうなると、閉じるボタンが押されたときの処理内容は、閉じるボタンのイベントハンドラに記述されることになり、この場合でいくとウィンドウ以外のものを表示したときに対応しにくくなります。
イベントリスナーを使用することで、イベントに対する処理を、リスナーを登録しているオブジェクト側に定義することができるため、より複雑な処理ができそうな気がしませんか?
上記の仕様で、画面を作ってみました。
まず、「ウィンドウを開く」ボタンをおすとウィンドウがでます。「閉じるボタン」を押下すると、すべてのウィンドウが閉じます。仕様通りです。
さて、それぞれのウィンドウが、「閉じるボタン」のwindowcloseallイベントに登録されています。そのときに、呼び出されるのが、Ext.Window.close()メソッドです。
それぞれのウィンドウは、windowcloseallイベントが発生したら、登録してあるcloseメソッドが呼び出され、ウィンドウを閉じます。
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
// +————————————————————————-+
// | JavaScript |
// +————————————————————————-+
// | Copyright (c) 2005 - 2008 Xenophy CO., LTD. |
// +————————————————————————-+
// | Authors: Kazuhiro Kotsutsumi <kotsutsumi@xenophy.com> |
// +————————————————————————-+// {{{ Ext.onReady
Ext.onReady(function(){
// ウィンドウインデックス
this.winIndex = 1;// ウィンドウオープンボタン生成
this.btnA = new Ext.Button({
text:‘ウィンドウを開く’,
renderTo:‘btnOpenWindow’,
listeners:{
click:{
scope:this,
fn:function(){// ウィンドウを生成
var win = new Ext.Window({
title : ‘Window’ + this.winIndex,
width : 300,
height: 200
});// イベントリスナー追加
this.btnB.addListener(
‘windowcloaseall’,
function() {
this.close();
},
win
);this.winIndex++;
win.show();
console.debug( "clicke" );
}
}
}
});// すべてのウィンドウ閉じるボタン生成
this.btnB = new Ext.Button({
text:‘すべてのウィンドウを閉じる’,
renderTo:‘btnCloseAllWindow’,
listeners:{
click:{
fn:function(){
this.fireEvent( ‘windowcloaseall’ );
}
}
}
});
});// }}}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* c-hanging-comment-ender-p: nil
* End:
*/
こうすることで、「閉じるボタン」側は、「すべてとじろー」と叫ぶ(発火する)だけで、あとはそれに登録されているリスナーたちが処理を行うわけです。これで、ウィンドウが何個か?ということは、「閉じるボタン」側は、意識する必要がありません。
今回作成したコード
こちらに、今回作成したコードを載せておきます。あとでソースコードレベルでゆっくりみてみてください。
まとめ
上記のような場合のように、イベントリスナーを使うと、処理したいオブジェクトを意識しなくてすみます。つまり、いちいちインスタンス化してオブジェクトをプロパティに保持したりせずとも、対象のオブジェクトがどこにインスタンス化されていようが、発火する側が意識せずにすむわけです。
僕の場合、いろいろなところで使っていますが、近々では「言語切り替え」に使用しました。
ロケール管理するクラスインスタンスに、パネルやらウィンドウやらコンポーネントやらがリスナー登録していくわけです。
ロケール管理するクラスは、「言語切り替え!」と発火する・またはされると、登録されているパネルたちが、自信の登録されてメソッドを使って、タイトルやらテキストやらを日本語から英語に変えたり、その逆をしたりしています。
処理を相手側に委譲できるってのは、ActionScriptとかでもそうですが、JavaScriptでもやっぱ便利です。
これがなかったらオブジェクトインスタンスを線で結んでスパゲティ上になってしまって、作りたいものを作るのに倍以上の労力がかかると思います。
なんで、ExtJSを使うときには、イベント制御を是非ご活用ください。
Pingback: Ext Japan - ブログ