SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

「Dojo道場」~実用アプリ構築のためのベストプラクティス

Dojo道場 ~ 第7回「カスタム・ウィジェット作成のためのウィジェット・ライフサイクル詳説」

「Dojo道場」~実用アプリ構築のためのベストプラクティス


ライフサイクル・メソッドの実装例

 さて、いよいよここからライフサイクル・メソッドの実装例を説明していきます。前節で説明した ProfileCard1 を元に、データのバリデーション、マウス・クリックのアクションの追加、ツールチップの表示などの機能を追加していきます。ウィジェットのソースはサンプル・ファイル中の examples/widget/ProfileCard2.js、テンプレートは examples/widget/templates/ProfileCard2.html、表示用の HTML は profileCard2.html です。

constructor

メソッド名 説明
constructor 通常のJavaScriptのオブジェクトと同様のコンストラクタメソッドです。ウィジェットのもつプロパティの初期化などを行います。

 以下の例では、title プロパティの初期値を設定しています。

リスト6. constructor 実装例
constructor: function(){
	this.title = "プロフィール";
}

 この例では文字列の初期化なので、リスト7のように、プロパティの宣言時に初期値を設定しても良いのですが、リスト8のように、プロパティがオブジェクトや配列の場合、宣言時に初期化を行うとおかしなことが起こります。

リスト7. 文字列を宣言時に初期化
dojo.declare("MyWidget", ...
	title: "プロフィール",
...
);
リスト8. オブジェクトの初期化の悪い例
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 の中で初期化します。

リスト9. オブジェクトの初期化の正しい例
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)を呼びましょう。

リスト10. postMixInProperties 実装例
postMixInProperties: function(){
	this.inherited(arguments);
        // emailに@が含まれない場合、不正な値として初期化する
	if(this.email && this.email.indexOf("@") < 0){
		this.email = "";
	}
}
補足: constructor と postMixInProperties

 constructorでもpostMixInPropertiesでもやることはほぼ同じじゃないか?と感じられるかもしれません。実際にどちらに書いても問題なく機能するケースがほとんどです。しかし実は両者には決定的な違いがあります。dojo.declareを使うとオブジェクト指向プログラミングで言うところの複数継承のようなことが可能なのですが、複数継承した場合、constructorは親クラスも含めた全てが呼ばれますが、postMixInPropertiesを含むライフサイクル・メソッドは、継承した一番最後の親のメソッドしか呼ばれません。

 以下の例で、constructorはSuper1、Super2の両方が呼ばれますが、postMixInPropertiesはSuper2のものしか呼ばれません。つまり、作成するカスタム・ウィジェットが別のカスタム・ウィジェットによってサブ・クラスされる場合でも、通常はconstructorに書かれたコードは必ず呼ばれることが保証されます。

リスト11. 複数継承時の constructor と postMixInProperties の振る舞いの違い
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)を非表示にしています。

リスト12. buildRendering の実装例
buildRendering: function(){
	this.inherited(arguments);
        //srcがパラメーターとして与えられていない場合、プロフィール写真のimgノードを隠す
	if(!this.src){
		dojo.style(this.imgContainerNode, "display", "none");
	}
}

postCreate

メソッド名 説明
postCreate ウィジェットの生成が終了した後に実行されます。イベント・ハンドラの定義などを行います。

 この時点でウィジェット単体の生成はほぼ完了しています。constructor、postMixInProperties、buildRendering のいずれにも入れることの出来ない実装をここに入れます。例えばイベント・ハンドラの定義や、状態を保持するようなウィジェットの場合は初期状態を与える、などの実装が考えられます。どのライフサイクル・メソッドに実装するのか迷った時は、ここに実装すれば多くの場合問題ありません。以下の例では、条件に応じてツールチップを生成し、また、「詳細」をクリックされた時のイベント・ハンドラのコネクトを行っています。

リスト13. postCreate の実装例
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");
}

次のページ
まとめ

修正履歴

この記事は参考になりましたか?

「Dojo道場」~実用アプリ構築のためのベストプラクティス連載記事一覧

もっと読む

この記事の著者

手島 拓也(テジマ タクヤ)

日本アイ・ビー・エム株式会社ソフトウェア開発研究所に勤務。現在は、検索およびテキスト解析ソフトウェア製品のUI開発を担当。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

井上 基晴(イノウエ モトハル)

日本アイ・ビーエム株式会社 ソフトウェア開発研究所。現在は主にDojoを用いた製品開発を担当。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/5879 2012/01/30 11:47

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー