Sencha Touch のクラスシステムには、プロパティの getter / setter を自動生成し、変更前に値を検証するメソッド(applier)、値の変更後に実行するメソッド(updater)を呼び出す機能が備わっています。今回は、この updater のハマりポイントに関するトピックです。
Problem:
コンポーネントの hidden プロパティや disabled プロパティの updater(updateHidden、updateDisabled)を定義したが、値を変更してもメソッドが呼び出されない。
Solution:
プロパティの updater として実行するために、doSet{$プロパティ名} メソッドを定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Ext.define('EventedConfigTest', { // *1 extend: 'Ext.Component', // *2 applyHidden: function(hidden) { // *3 console.log('apply:' + hidden); return hidden; }, updateHidden: function(hidden) { // *4 console.log('update:' + hidden); }, doSetHidden: function(hidden) { // *5 console.log('doSet:' + hidden); this.callParent(arguments); } }); var comp = Ext.create('EventedConfigTest'); // *6 comp.getHidden(); // *7 // > null comp.setHidden(true); // *8 // > apply:true // *9 // > doSet:true // *10 comp.getHidden(); // *11 // > true comp.setHidden(true); // *12 // > apply:true // *13 |
*1 ”EventedConfigTest” という名前のクラスを定義
*2 ”Ext.Component” クラスを継承
*3 hidden プロパティに対する applier を定義
*4 hidden プロパティに対する updater を定義
*5 hidden プロパティの updater として実行するために doSetHidden メソッドを定義
*6 ”EventedConfigTest” クラスのインスタンスを生成
*7 インスタンスの hidden プロパティの値を確認。このタイミングでは値に null が設定されている
*8 インスタンスの hidden プロパティに true を設定する
*9 applier が実行される
*10 updater は実行されない。定義した doSetHidden は実行される
*11 インスタンスの hidden プロパティの値を確認。値に true が設定されている
*12 もう一度、インスタンスの hidden プロパティに true を設定する
*13 applier が実行される。値の変更がないので、doSetHidden は実行されない
Discussion:
実は、コンポーネントのプロパティには、通常のプロパティの他に Evented なプロパティがあります。Ext.Component のソースコードを見ると、config オプションの他に eventedConfig オプションを定義していることが分かります。eventedConfig オプションは、初期化時の change アクションの実行を抑制するために利用されています。初期化時のパフォーマンス改善が目的のようです。
この eventedConfig オプションで定義したプロパティは config オプションで定義したプロパティと同様、getter / setter が自動生成されるのですが、挙動が微妙に異なります。そして最大の違いは、なんと updater が実行されないことです。。内部ではプロパティの値を変更した後、updater と似た用途で doSet{$プロパティ名} メソッドを実行しています。そのため、doSet{$プロパティ名} メソッドを定義することで updater の代替にできる、という解決策でした。
この解決策を採る際、doSet{$プロパティ名} メソッドで親クラスの同名メソッドを実行することを忘れないようにして下さい(具体的には、this.callParent(arguments) を忘れずに記述して下さい)。そうしないと本来実行されるべき処理が実行されなくなってしまいます。
正直なところ、doSet{$プロパティ名} メソッドの継承は、公式には推奨されていません。doSet{$プロパティ名} メソッドを継承するより、set{$プロパティ名} メソッドを継承して、処理の後、自前で updater 用に定義したメソッドを呼ぶ方が、バージョンアップで仕様が変わった際に安全かもしれません。微妙なノウハウですが、Evented なプロパティで updater が実行されないケースは結構ハマりがちだと思うので、今回ご紹介させて頂きました。
※個人的には、Evented なプロパティの場合でも、doSet{$プロパティ名} メソッドではなく、update{$プロパティ名} メソッドを実行するように仕様変更されることを願います。。
See Also:
How to Use Classes in Sencha Touch:
http://docs.sencha.com/touch/2-1/#!/guide/class_system
Sencha Touch ソースコード:
Evented.js
AbstractComponent.js
Component.js
core/class/Class.js
最近のコメント