よくある「コード入力で名称表示」部品をdojoで作ってみる

dojoはすでにRelease Versionが1.1となっていて、昨年から今までにかけて、かなりのスピードで進化している。昔のVersion 0.4.3と比較すると、コンポーネントの作り方などに大きな変更が入っていたりする。本職でdojoを使う機会も多いので、ここでは代表的なカスタムコンポーネントの作り方を紹介してみよう。

代表例とは「コード入力とその名称の表示」部品。商品コードを入力すると、その脇に商品名が自動的に表示される、というものだ。

ちなみに、dojoは以下のサイトから1.1を入手する。

http://dojotoolkit.org/downloads

入手後、Webサイトの任意のコンテキスト内に配置する。多くの場合、jsとかjavascriptsとか、JavaScriptコードのファイルを配置する専用のディレクトリを作成する。今回は、以下のようなレイアウトとしてみる。

+– index.html
+– js
 +– dojo
 +– dijit
 +– dojox
 +– util
 +– jp
  +– eisbahn
   +– CodeInputField.js
   +– CodeInputField.html

最初に、CodeInputField.jsファイルから。

dojo.provide()関数により、このモジュールの宣言を行い、dijit.form._FormWidgetを継承して、dojo.declare()関数によりjp.eisbahn.CodeInputFieldクラスを宣言している。_FormWidgetクラスを継承することにより、getValue()およびsetValue()関数の作成が求められる。コード入力フィールドのonChangeイベントに対応するために、_onChange()関数を作成してある。ここでは、入力値によって、固定的に名称を表示している。

上記のコード中で、this.codeやthis.displayといった変数を使用しているが、これが何を指すのかは上記だけではわからない。templatePathに、CodeInputField.htmlファイルを指定しているのだが、このHTMLファイルに答えが隠されている。

このHTMLは、CodeInputField部品のテンプレート、つまりHTMLの断片となる。${…}で囲まれた部分は、このコンポーネントを使用する際に指定したパラメータ値で置換される。これについては、index.htmlファイルを見ていくときに再度紹介するとして、ここではdojoAttach〜に注目。

まずはdojoAttachPoint属性。これにより、JavaScriptコード中から対象DOM要素を変数でアクセスできるようになる。dojoAttachPoint=”code”およびdojoAttachPoint=”displayName”が付加されている要素が、先のthis.〜で参照していたものになる。JavaScriptコードの中でdocument.getElementById()などを使用する必要がなくなり、さらにこのコンポーネントが複数個配置された場合なども気にせず使えるので、非常に便利な機能だ。

さらに、dojoAttachEvent属性も非常に強力。これによって、対象DOM要素でのイベントハンドラを登録することができる。ここで注意点として、”onChange:_onChange”ではダメ。イベント名は全て小文字にして記述しないといけない。これのために半日を無駄にすることも多々あるので、dojoAttachEvent使用時には最新の注意が必要だ。

最後に、index.htmlを見てみよう。これは、CodeInputField部品を実際に使う記述がなされる。

基本的に、dojoType属性にCodeInputFieldクラス名を記述するだけで、そのdiv要素がコード入力部品に自動的に置き換わる。テンプレートのHTML内で”${name}”や”${label}”という記述があったが、上記のname属性やlabel属性の値で置き換わる。部品を使う側でカスタマイズができるようになるので、活用すべきものだろう。

JSPのカスタムタグを開発しているような感覚を、dojoが提供してくれる。これは実際の大規模案件でも非常に有効なテクニックなので、ぜひチャレンジしてみてほしい。

dojo-release-0.9を見てみた

今年も気がつくともう10月。振り返ってみると,dojoを使う機会がすごく多くなった。とはいえ,使用しているのはdojo-0.4.2とか0.4.3。そろそろdojo-1.0が登場する予定なこともあって,0.9を見ておかないといけないぞ,と。dojo使いまくりのこみゅすけの起動速度が遅くなってきているのも気になるので,dojo-release-0.9を試してみることにした。
0.4.3から0.9の差分を中心として,ぱっと見で気になった点を以下にまとめてみる。

  • dojo一個だったのが,dojo, dojox, dijit, utilと4分割されている。
  • テーマが導入された結果,Widget毎だったCSSファイルが0.9では統一されている。
  • dojo.widget.defineWidget()でWIdgetクラスを定義していたが,0.9からはdojo.declare()を使う。
  • WIdgetの親クラスとしてHtmlWidgetを指定することが多かったが,0.9からは以下のクラスを組み合わせて親クラス指定をする。

    • dijit._Widget
    • dijit._Templated
    • dijit._Container
    • dijit._Contained
    • dijit.layout._LayoutWidget
    • dijit.form._FormWidget
  • dijitには「Window Systemを目指しているわけではない」という理由で,FloatingPaneが含まれていない(dojoxに含まれている)。
  • Google Map,Yahoo Mapは,dhartingのPortingのあとに,dojox.geoとして作成をするつもりらしく,0.9では含まれていない。
  • Widgetを使用する際に,createWIdget()を使うのではなく,0.9からはWidgetクラスをnewするだけで良い。

特にカスタムWidgetを作る際の手順が,0.9でかなり変わっている。特に「親クラスとして何を指定するか」や「CSSをどこで定義すればいいか」,「テーマを使えるようにしておくにはどうすればいいか」などが,ぱっと見では見えてこない。
勉強して今後レポートしていくつもりである。

Safariでdojoを使うと文字化けする件

こみゅすけは,dojoというライブラリをかなりの勢いで使用している。dojoは数多くあるJavaScriptライブラリの中で,おそらく最も大きなコードセットだろう。イベント処理やIO関連,各種ウィジェットなど,Ajaxでアプリケーションを作る上で必要となる全ての分野について網羅している。今まではそのコードの大きさのために「遅いっ!!」という悪印象があり,利用を敬遠してきた人も多くいることだろう。しかし,0.4.2以降では,テンプレートの読み込みの削減など,動作の遅さを根本解決するための修正が施されているために,結構軽くなってきたな,と個人的には思っている。次の0.9もβ版がそろそろ登場するはずなので,世間も今後dojoを採用することを積極的に検討しても良いのでは,と真面目に思っている。

しかし,dojoを使う上で,1点だけやばいことがある。それは,

「Safariで文字化けしちゃう」

ということだ。dojoを使う時に,簡単なものであれば,htmlファイル内にスクリプトを含め全て書いてしまえば問題はない。しかし,dojoを使い込んでいくためには,jsファイルをどんどん作っていって,例えばdojoのウィジェットを継承してカスタムコンポーネントを作ったりしていくことが求められる(と書くと難しそうだけど,わかってしまえば決して難しいことではない)。で,そのjsファイルや,ウィジェットが使用するテンプレートのhtmlファイルの中に日本語を直書きしてしまうと,見事にSafariは文字化けしてしまう。以下が文字化けのスナップショットである。見るも無惨だ。

commusuke-safari1.jpg

この文字化け問題,dojoに限った話ではなく,Ajaxの結果をresponseTextで取得する際にUTF-8が正しく処理されずに化ける,という結構有名な話である。下記のURLを叩けばわかるのだが,ググってみるとかなりヒットする。実際にこみゅすけで体感するまで,僕はこの問題を知らなかった。心より恥じる。

「safari ajax 文字化け」のGoogle検索結果

で,解決策としては,以下のページが参考になる。

さて,自分でAjax回りのコーディングをしているのであれば,上記で示されている対処を施すのは簡単である。しかし,dojoは違う。リッチすぎて,通信回りの処理はdojoの奥底に隠されているのだ。「dojo.require」している箇所や,dojo.widget系のtemplatePathでテンプレートファイルを指定している場合は,その読み込みはdojoが自動的にやってくれるため,上記の対処方法を「dojoをいじることなしに」適用することは残念ながら不可能である(頑張ればできるかもしれないけど,僕はそこまで頑張れる自信がないorz)。

というわけで,dojoのコードを追っていって,文字化け解消のためのコードを埋めてみる(= dojoをいじる)ことにした。修正は2ヶ所。

まず,「dojo.require」で読み込まれるファイルについての文字化け対応から。dojo.jsファイルに書かれているloadUri()関数を修正する。dojo 0.4.3では,dojo.jsファイルの209行目あたり。

getText()関数の戻り値は,resnponseTextで得られた文字列が返される。これに対して,「Safari の responseText で UTF-8 コード文字化け回避」で示されている対処法を適用している。これだけで,普通にdojoを使っているアプリであれば,Safariでも文字化けすることなく動作するようになる。

次に,例えば,

というように,自作テンプレートファイルを使うようにしていて,上記であればBlogLink.htmlファイル中に日本語を直書きしていた場合,SafariではやはりresponseText問題となってしまう。これの対処として,widget/DomWidget.jsファイルのfillFromTemplateCache()関数を修正する。dojo 0.4.3では,54行目あたり。

元々「var tstring = this._sanitizeTemplateString(dojo.hostenv.getText(tpath));」とされている処理を,やはりgetText()関数の戻り値であるresponseTextの文字列について,同様の対処策を施すようにしている。これで,templatePathに自作テンプレートファイルを適用している場合についても,文字化けすることなく動作するようになる。

上記の対処を行うことにより,こみゅすけもSafariで元気に動作するようになった。

commusuke-safari2.jpg

「もうみんなFirefoxでいいじゃん」と,「Firefox以外のWebブラウザは滅びてくれ」と,「Firefoxでなくてもいいから,どれか1つにしようよ」と,冗談抜きでそう思う。orz

カタログページにDojoあり

カタログページに関するサーバ側の風景」のエントリで説明したカタログページの表示のためのサーバ側の処理は,サーブレットとJPA(Java Persistence API)の組み合わせによって実装されていた。CatalogServletサーブレットでは,Webブラウザからの要求情報に含まれるcommandパラメータの内容によって処理分岐し,CatalogFacadeインスタンスの所定の処理を呼び出して,その結果をXMLあるいはJSON形式で返却するという処理が行われていた。
では,CatalogServletサーブレットに要求を出す側の処理はどのようなものだろうか。これを知るためには,カタログページでのイベント処理を担当するcatalog.jsファイルを見ていく必要がある。
ざっくりとcomandという文字列でcatalog.jsファイル内を検索してみる。すると,125行目付近に,以下のようなコードを見つけることができる。

var bindArgs = {
  url: “/petstore/catalog?command=categories&format=json”,
  mimetype: “text/json”,
  load: function(type,json) {
    ac.load(json);
    processURLParameters();
  }
};
dojo.io.bind(bindArgs);

「dojo.io.bind(bindArgs);」という文が今回のメインだ。これは,サーバに処理要求を送信するために,Dojoが持つioパッケージのbind()関数を使用している箇所である。これによって,俗に言うAjaxでの非同期通信が行われる。Ajaxというと,XMLHttpRequestオブジェクトなどを利用したコードがまだまだ主流だが,prototype.jsなどと同じように,そのような低レベルAPIを隠蔽したものにDojoも仕上がっている。
通信内容および処理結果に対する処理は,bind()関数の引数として渡しているbindArgs変数の内容に従って決定される。bindArgs変数の内容は,この例では以下のものが定義されている。

  • url – サーバに送信するURL文字列
  • mimetype – 処理結果の形式
  • load – 処理結果を受信した際に呼び出す関数

urlを見ると,commandパラメータにcategoriesという値を指定しているのがわかる。/petstore/catalogというパスはCatalogServletサーブレットにマッピングされているので,ここでは全てのカテゴリ情報の取得をCatalogServletサーブレットに依頼していることになる。
さらにここでは,formatパラメータにjsonという値を指定し,mimetypeとしてtext/jsonとしている。CatalogServletサーブレットのhandleCategories()メソッドでは,formatパラメータの値がjsonだった時は,JSON形式で処理結果を生成する動きになっている。mimetypeにtext/jsonを指定することで,CatalogServletサーブレットからの処理結果文字列を,JavaScriptのオブジェクトに自動的にDojoが変換してくれる。
AjaxというとXMLでの結果取得を真っ先に想像するが,XMLのパース処理やJSON形式の文字列をeval()関数でオブジェクト化する処理とのコストパフォーマンスおよびコードの可読性の比較を考えると,JSON形式の方がJavaScriptがメインの実装方針では優位性があるだろう。
bind()関数は基本的に非同期通信なので,CatalogServletサーブレットからの処理結果を受けとるためのコールバック関数が必要となる。ここでは,loadに直接関数オブジェクトを定義して渡している。第2引数のjsonには,自動的に変換されたCatalogServletサーブレットの処理結果,つまりカテゴリの一覧が渡されるので,それをaccordion.jsファイルに定義されているload()関数に渡して,カタログページの左側にあるペットのカテゴリ一覧のレンダリングなどを行っている。
ここまでの流れを簡単に図にすると,以下のような感じだ。
categories-json.gif
非同期通信とJSON関連の処理をカプセル化してくれているので,非常に使い勝手が良い。dojo.io.bind()関数は,Dojoの中でも非常に多用される機能であり,catalog.jsファイル中でも他の箇所で多く見つけることができる。bind()関数は,他にもいろいろなプロパティを使用できたりするので,「io.html – dojo manual」を参考にして試してみると良いだろう。

PetStoreで使われている「FisheyeList」Dojoウィジェット

Java PetStoreのトップページは,ペットの種類を選択する画面となっている。この画面では,中央に鳥のイメージとその周囲に各ペットの種類を表すイメージが配置されている。そして何よりも目を引くのが,左側にあるペットの種類のイメージリストだ。この中にマウスカーソルを合わせると,マウスカーソルの下にあるイメージが盛り上がるようにズームアップされる。更にマウスカーソルを上下に移動させると,その盛り上がりがマウスカーソルに追随されるような効果を見ることができる。まるで,Mac OS XのDockのような効果である。
FisheyeList1.gif
これは,Dojoのウィジェットを使うことによって実現されている。使われているウィジェットは「FisheyeList」。魚眼リスト,と日本語で直訳できるが,ようは凸レンズを通して見たときのような効果を得られるウィジェットだ。
このページは,petstore/Web Pages/index.jspファイルにより構成される。index.jspファイル中の<script>タグ中に,さっそくFisheyeListというキーワードを見つけることができるだろう。ちなみに,dojo.jsファイルは,banner.jspファイルによりロードされている(「PetStoreはDojoでいっぱい」参照)。

<script>
dojo.require(“dojo.widget.FisheyeList”);
function browse(category) {
  window.location.href=”/petstore/faces/catalog.jsp?catid=” + category;
}
</script>

Dojoに搭載されている各種モジュールを使用するには,dojo.require()メソッドを使って,予め使用を宣言しておく必要がある。これは,必要な分だけWebブラウザにコードを読み込むようにするためである(といってもdojo.jsだけでも大きいけど)。index.jspでは,dojo.widget.FisheyeListという文字列をrequire()メソッドに渡すことにより,FisheyeListウィジェットを読み込んでいる。
そして,実際にFisheyeListウィジェットを適用している箇所が,以下のコードだ。

<div class=”dojo-FisheyeList”
  dojo:itemWidth=”170″ dojo:itemHeight=”50″
  dojo:itemMaxWidth=”340″ dojo:itemMaxHeight=”100″
  dojo:orientation=”verticle”
  dojo:effectUnits=”2″
  dojo:itemPadding=”10″
  dojo:attachEdge=”center”
  dojo:labelEdge=”bottom”
  dojo:enableCrappySvgSupport=”false”>

 <div class=”dojo-FisheyeListItem” onClick=”browse(‘Dogs’);”
   dojo:iconsrc=”/petstore/images/dogs_icon.gif”>
 </div>

 <div class=”dojo-FisheyeListItem” onClick=”browse(‘Cats’);”
   dojo:iconsrc=”/petstore/images/cats_icon.gif”>
 </div>

 ・・・
<div>

<div>タグのclass属性にdojo-FisheyeListを指定することにより,この要素がFisheyeListウィジェットとして適用される。そして,FisheyeListウィジェットが必要とする各種パラメータを,dojo名前空間で修飾された各種属性によって与えている。属性の意味は,以下の通り。

  • itemWidth, itemHeight – 通常時(マウスカーソルが乗っていないとき)の大きさ
  • itemMaxWidth, itemMaxHeight – 最大時(マウスカーソルが乗っているとき)の大きさ
  • orientation – アイテムを並べる方向(horizontal=水平,それ以外=垂直)
  • effectUnits – アイテムの大きさが変化するときの増分値
  • itemPadding – アイテム間の間隔
  • attachEdge – アイテムの効果の基準となる軸
  • labelEdge – キャプション文字列を表示する位置
  • enableCrappySvgSupport – 謎

大体どれも想像がつくのではないだろうか。attachEdge属性などを変更すると,その効果がわかりやすい。例えば,現在centerとなっているのをleftにすることで,アイテムの左端を基準として,アイテムが膨張されるようになる。
FisheyeList2.gif
dojo-FisheyeListが指定された<div>タグの子要素として,各アイテムを定義していく。アイテムは,class属性にdojo-FisheyeListItemを指定する。アイテムが持つ属性は,以下の通り。

  • onClick – アイテムがクリックされたときに呼び出されるイベントハンドラ
  • iconsrc – アイテムとして表示するイメージのパス
  • caption – アイテムがフォーカスされたときに表示するキャプション文字列

caption属性は使われていないが,例えば,iconsrc属性にcats_icon.gifファイルを指定している<div>タグを以下のように変更してみる。

 <div class=”dojo-FisheyeListItem” onClick=”browse(‘Cats’);”
   dojo:iconsrc=”/petstore/images/cats_icon.gif”
   dojo:caption=”Nyah!”>
 </div>

さらに,labelEdge属性をbottomからrightに変更すると,以下のようにキャプションが表示されるようになる。
FisheyeList3.gif
イメージの伸縮は,FisheyeListウィジェットが勝手にやるので,僕らはアイテムとしたい画像ファイルをそれぞれ用意するだけで良い。あとは各アイテムの<div>タグのonClick属性で,アイテムがクリックされたときの動作を記述するだけで,クールなメニューが出来上がる。なんとも楽ちんだ。
ちなみにPetStoreでは,petstore/Web Pages/styles.cssファイル中に以下のように記述することにより,アイテムにマウスカーソルを置いた際にマウスポインタが変更されるようにしている。

.dojoHtmlFisheyeListBar {
  cursor:pointer;
  margin: 0;
  width: 300px;
  text-align: right;
}

PetStore 2.0ea1の中で使われているDojoのウィジェットは,とりあえずFisheyeListだけのようだ。しかし,このFisheyeListウィジェットを見ただけでも,Dojoの潜在能力を知ることができる。難しいこと抜きにして,Dojoってとっても面白そうだ。
FisheyeListウィジェットをトップページに配置するあたり,PetStore作者の気持ちが伝わってくる。

PetStoreはDojoでいっぱい

Java PetStore 2.0ea1のインストール」によって動作を開始したJava PetStore。画像のエフェクトの種類が多く,触っていてなかなか面白い。どこでAjaxが使われているのか,さくっと触っただけではよくわからないが,Webブラウザ上でスクリプトが盛り沢山なのだけは明確にわかる。あまりスクリプトを書いたことのない僕としては,なかなか手ごわそうだ。
さて,さっそくコードを追っていきたいのだが,まず考えることといえば,どのファイルから見ていけばいいのか,ということだ。Webアプリケーションなんだから最初はindex.htmlだろうな,と勝手な推測は見事に的中。petstore/Web Pages/index.htmlファイルが,PetStore Demoを説明したページのファイルだった。
説明ページには,確か「Enter the Store」というリンクがあった。探してみると,以下のようになっていた。

<h2><a href=”/petstore/faces/index.jsp”>Enter the Store</a></h2>

なるほど,index.jspがPetStoreの本当の入り口のようだ。おもむろにpetstore/Web Pages/index.jspを見てみると,<body>タグまでは普通のHTMLだが,さっそく<script>タグが登場する。

<jsp:include page=”banner.jsp” />
<script>
dojo.require(“dojo.widget.FisheyeList”);
function browse(category) {
 window.location.href=”/petstore/faces/catalog.jsp?catid=” + category;
}
</script>

dojoというオブジェクトのrequireメソッドを呼んでいる。dojoって聞いたことがある。なんだっけ?
Dojoは,Ajax時代のインタラクティブなWebアプリケーションを実現するためのJavaScriptライブラリである。豊富なWidgetの提供が特徴であり,Sunなどの企業がスポンサーとなっているため,今後の発展も期待できる注目すべきライブラリだ。と,完全に知ったかぶりをしてみたが,なるほど,PetStoreはDojoを使っているのか。
JavaScriptでは,大きなライブラリであれば,jsファイルにメソッド群がまとめられて,<script>タグのsrc属性を使ってjsファイルを読み込まれているのがセオリー。しかし,index.jspファイルには,それが見当たらない。気になるのが,上記のコードでbanner.jspファイルをインクルードしている点。もしや,banner.jspがくせ者なのか。
またも狙いは的中し,petstore/Web Pages/banner.jspファイルで,dojo.jsファイルを読み込んでいた。

<script type=”text/javascript” src=”/petstore/dojo.js”></script>

banner.jspファイル内では,バナーにPetStoreに関するRSSを表示するためのrss.jsファイルも読み込んでいる。banner.jspファイルでは,RSSの表示に関する処理をdojoオブジェクト読み込み時に実行することを割り当てるスクリプトが書かれている。RSSの表示に関する部分はまだよく見ていないので後回し。
index.jspファイルに戻ると,dojoというキーワードがあちこちにあることがわかる。<script>タグ内では,”dojo.widget.FisheyeList”というウィジェットを要求しているようだ。そして,

<div class=”dojo-FisheyeListItem” onClick=”browse(‘Dogs’);”
 dojo:iconsrc=”/petstore/images/dogs_icon.gif”>
</div>
<div class=”dojo-FisheyeListItem” onClick=”browse(‘Cats’);”
 dojo:iconsrc=”/petstore/images/cats_icon.gif”>
</div>
<div class=”dojo-FisheyeListItem” onClick=”browse(‘Birds’);”
 dojo:iconsrc=”/petstore/images/birds_icon.gif”>
</div>
<div class=”dojo-FisheyeListItem” onClick=”browse(‘Fish’);”
 dojo:iconsrc=”/petstore/images/fish_icon.gif”>
</div>
<div class=”dojo-FisheyeListItem” onClick=”browse(‘Reptiles’);”
 dojo:iconsrc=”/petstore/images/reptiles_icon.gif”>
</div>

という箇所を見ると,どうもトップページの左側にある,まるでMacのDockのようなエフェクトの,ペットの種類を選ぶ部分でDojoが使われているようだ。
index.jspファイル内の<script>タグの中を見ると,browseメソッドの中でcatalog.jspファイルを呼び出している。index.jspページの中の具体的なペットの種類をWebブラウザ上でクリックしたときに呼び出されるメソッドがbrowse()メソッドだ。つまり,catalog.jspファイルは,具体的なペットを選ぶためのカタログページを表示するjspファイルだ。
petstore/Web Pages/catalog.jspファイルを開くと,このファイルでもbanner.jspファイルをインクルードしているので,必然的にdojo.jsファイルを読み込むことになる。dojo.jsファイル以外にも,scroller.jsファイルやaccordion.jsファイル,それにcatalog.jsファイルも読み込んでいることがわかる。

<script type=”text/javascript” src=”scroller.js”></script>
<link rel=”stylesheet” type=”text/css” href=”scroller.css”></link>
<script type=”text/javascript” src=”accordion.js”></script>
<link rel=”stylesheet” type=”text/css” href=”accordion.css”></link>
<script type=”text/javascript” src=”catalog.js”></script>

catalog.jspファイルの中では,dojoというキーワードは一つしか出てこない。しかし,読み込んでいる3つのjsファイルの中では,dojoというキーワードをいくつも発見することができる。しかし,widgetという単語ではなく,ioやbind,publishなどといった単語が見受けられるため,イベント関連やサーバとの通信でDojoが使われているようだ。
他のjspファイルではdojoという単語は見受けられないが,jsファイルを見るとdojoという単語を多くの箇所で見つけることができる。
PetStoreの動作にDojoが根幹をなしていることは確かなようだ。

Get Adobe Flash playerPlugin by wpburn.com wordpress themes