ライフサイクル・メソッドの実装例
さて、いよいよここからライフサイクル・メソッドの実装例を説明していきます。前節で説明した ProfileCard1 を元に、データのバリデーション、マウス・クリックのアクションの追加、ツールチップの表示などの機能を追加していきます。ウィジェットのソースはサンプル・ファイル中の examples/widget/ProfileCard2.js、テンプレートは examples/widget/templates/ProfileCard2.html、表示用の HTML は profileCard2.html です。
constructor
| メソッド名 | 説明 |
| constructor | 通常のJavaScriptのオブジェクトと同様のコンストラクタメソッドです。ウィジェットのもつプロパティの初期化などを行います。 |
以下の例では、title プロパティの初期値を設定しています。
constructor: function(){
this.title = "プロフィール";
}
この例では文字列の初期化なので、リスト7のように、プロパティの宣言時に初期値を設定しても良いのですが、リスト8のように、プロパティがオブジェクトや配列の場合、宣言時に初期化を行うとおかしなことが起こります。
dojo.declare("MyWidget", ...
title: "プロフィール",
...
);
dojo.declare("MyWidget",dijit._Widget, {
prop: {} // 宣言時にオブジェクトを初期化
});
var o1 = new MyWidget();
var o2 = new MyWidget();
o1.prop.foo = "FOO";
console.log(o1.prop); //Object { foo="FOO"}
console.log(o2.prop); //Object { foo="FOO"}
意図せず o1 のプロパティの変更が o2 にも適用されてしまいました。これは dojo.declare で定義したプロパティは prototype にコピーされるためです。Javascript ではオブジェクトや配列はその参照が保持されるため、このような事が起こります。これを避けるために、オブジェクトや配列は以下のように constructor の中で初期化します。
dojo.declare("MyWidget",dijit._Widget, {
prop: null,
constructor: function(){
this.prop = {}; // constructor でオブジェクトを初期化
}
});
var o1 = new MyWidget();
var o2 = new MyWidget();
o1.prop.foo = "FOO";
console.log(o1.prop); //Object { foo="FOO"}
console.log(o2.prop); //Object {}
postMixInProperties
| メソッド名 | 説明 |
| postMixInProperties | ウィジェットのプロパティが挿入された後に実行されます。プロパティ値のバリデーションなどを行います。 |
以下の例では、emailプロパティの値の簡単なバリデーションを行っています。1行目のthis.inherited(arguments)はJavaで言うところのsuper()のことで、Dojoが提供している、親クラスの同名メソッドを呼び出すためのメソッドです。ライフサイクル・メソッドの実装時には、特に理由が無い限り、必ずthis.inherited(arguments)を呼びましょう。
postMixInProperties: function(){
this.inherited(arguments);
// emailに@が含まれない場合、不正な値として初期化する
if(this.email && this.email.indexOf("@") < 0){
this.email = "";
}
}
constructorでもpostMixInPropertiesでもやることはほぼ同じじゃないか?と感じられるかもしれません。実際にどちらに書いても問題なく機能するケースがほとんどです。しかし実は両者には決定的な違いがあります。dojo.declareを使うとオブジェクト指向プログラミングで言うところの複数継承のようなことが可能なのですが、複数継承した場合、constructorは親クラスも含めた全てが呼ばれますが、postMixInPropertiesを含むライフサイクル・メソッドは、継承した一番最後の親のメソッドしか呼ばれません。
以下の例で、constructorはSuper1、Super2の両方が呼ばれますが、postMixInPropertiesはSuper2のものしか呼ばれません。つまり、作成するカスタム・ウィジェットが別のカスタム・ウィジェットによってサブ・クラスされる場合でも、通常はconstructorに書かれたコードは必ず呼ばれることが保証されます。
dojo.declare("Super1", dijit._Widget, {
constructor: function(){
console.log("Super1:constructor");
},
postMixInProperties: function(){
console.log("Super1:postMixInProperties");
}
});
dojo.declare("Super2", dijit._Widget, {
constructor: function(){
console.log("Super2:constructor");
},
postMixInProperties: function(){
console.log("Super2:postMixInProperties");
}
});
dojo.declare("Sub1", [Super1, Super2], {});
var sub = new Sub1();
// Super1:constructor
// Super2:constructor
// Super2:postMixInProperties
buildRendering
| メソッド名 | 説明 |
| buildRendering | ウィジェットの基本となるDOMを構築するためのメソッドであり、テンプレートの仕組みを実現するためのメソッドと言っても過言ではありません。 |
テンプレートを使わないカスタム・ウィジェットの場合はここでDOMを構築していきます。テンプレートを使うカスタム・ウィジェットの場合は、テンプレートの処理後に構築されたDOMをさらにカスタマイズする場合に利用します。リスト12の例では、srcプロパティが設定されていない場合に、画像を含むタグ(this.imgContainerNode)を非表示にしています。
buildRendering: function(){
this.inherited(arguments);
//srcがパラメーターとして与えられていない場合、プロフィール写真のimgノードを隠す
if(!this.src){
dojo.style(this.imgContainerNode, "display", "none");
}
}
postCreate
| メソッド名 | 説明 |
| postCreate | ウィジェットの生成が終了した後に実行されます。イベント・ハンドラの定義などを行います。 |
この時点でウィジェット単体の生成はほぼ完了しています。constructor、postMixInProperties、buildRendering のいずれにも入れることの出来ない実装をここに入れます。例えばイベント・ハンドラの定義や、状態を保持するようなウィジェットの場合は初期状態を与える、などの実装が考えられます。どのライフサイクル・メソッドに実装するのか迷った時は、ここに実装すれば多くの場合問題ありません。以下の例では、条件に応じてツールチップを生成し、また、「詳細」をクリックされた時のイベント・ハンドラのコネクトを行っています。
postCreate: function(){
this.inherited(arguments);
//顔写真とコメントがパラメーターとして与えられている時にツールチップウィジェットを生成する
if(this.src && this.tooltipComment){
this.tooltip = new dijit.Tooltip({connectId:this.imgContainerNode, label: this.tooltipComment}, this.tooltipNode);
}
//詳細リンクのonclickイベントにハンドラーをコネクトする
this.connect(this.detailLinkNode, "onclick", "_onClickDetailLink");
}
