Wicketで始めるオブジェクト指向ウェブ開発
第8回 組み込みAjax機能で動的に変化するページを実現する
今回のテーマは,Wicketの組み込みAjax機能を使ってページ全体のリロードを無くすことです。最近のアプリケーションでは,JavaScriptおよびAjaxを使ってページを動的に作り替えることで,ページ全体のリロードを減らす方向にあります。Ajaxなしにアプリケーションを作れない時代になったと言えるでしょう。組み込みAjax機能はそのような動的なページ部分更新をWicket上で実現するためのものです。
組み込みAjax機能の位置づけ
あらかじめ注意しておくと,WicketはJavaScriptフレームワークでもありませんし,Ajax開発のためのフレームワークではありません。Ajaxだけに注目すると,組み込みAjaxの機能は不十分に見えることでしょう。実際,筆者はそういう指摘を受けたこともあります。JavaScript開発のための機能をWicketに求めるのは間違いです。JavaScriptの開発は,あなたのお気に入りのJavaScript用フレームワークを使って行うほうがずっと快適なはずです。WicketはJavaを使ってアプリケーションを構築するフレームワークです。今回紹介する組み込みAjax機能も,あくまでJavaからプログラムできる機能として提供されています。
WicketはJavaScript言語を使った開発のサポートは行いませんが,一方で,開発したJavaScriptソースファイルをコンポーネント内に閉じ込めるための機能は提供します。この機能を使って,コンポーネント使用者がJavaScriptの詳細を知ることなく使える「Wicketコンポーネント」を作ることができます。Wicketが提供するのは,コンポーネントの機能を実装するためにJavaScriptプログラムを利用するための「仕組み」なのです。
Wicketではページを部分的に更新するのは,特別なJavaScript操作を必要とする「つけたし」ではなく,フレームワーク自体に組み込まれた機能の1つです。その機能を実現するために裏ではAjaxを使っている,と考えると,組み込みAjax機能の位置づけが分かりやすいでしょう。今回の記事を読む際にも,JavaScriptのことは一旦忘れ,新しく紹介するWicketの機能を知るつもりで読むのが良いでしょう。
ページの部分更新を行う
Wicketでページの部分更新を行うには,AjaxRequestTargetオブジェクトが必要となります。AjaxRequestTargetオブジェクトはプログラマが自分で用意するものではなく,Wicketにより提供されるオブジェクトです。Wicketの提供するコンポーネントのうち,クラス名の先頭がAjaxで始まるコンポーネントがAjaxRequestTargetに対応しています。
例えば,ボタンをクリックした結果としてページの一部分だけを更新したい場合には,Linkコンポーネントの代わりにAjaxLinkコンポーネントを使います。LinkとAjaxLinkの使い方はほぼ同じで,どちらもユーザのクリックを検知すると自身のonClick()メソッドを呼び出します。ただし,onClick()にAjaxRequestTargetオブジェクトが渡されるかどうかだけが異なります。
リスト1 AjaxLinkのonClick()メソッド
new AjaxLink("link") {
@Override
public void onClick(AjaxRequestTarget target) {
//ここにクリック時の処理を書く
}
}
上記プログラムのとおり,変わったところはAjaxRequestTargetを引数として受け取っていることだけです。WicketがAjaxRequestTargetオブジェクトを作成して,onClick()に渡してくれます。
Linkコンポーネントでは,onClick()内部でモデルを適切に変更しさえすれば,ページ全体が再描画されることで,最新の状態が表示に反映されました。しかし,AjaxLinkでは何もしなければページは一切変化しません。変化させたい場所をWicketに伝えなくてはならないのです。
Wicketに変更箇所を指定する方法は,AjaxRequestTargetオブジェクトに再描画したいコンポーネントをaddComponent()メソッドでセットするだけです。
リスト2 リンクがクリックされたらlabelだけを更新する
final Label label = new Label("myLabel");
add(label);
(中略)
new AjaxLink("link") {
@Override
public void onClick(AjaxRequestTarget target) {
target.addComponent(label);
}
}
このプログラムでは,AjaxLinkをクリックするとaddComponent()メソッドでlabelコンポーネントをAjaxRequestTargetに登録しています。これだけで,Wicketが裏側でAjaxを利用して,labelコンポーネントが適用されているタグ部分だけを更新します。addComponent()は何度でも呼ぶことができます。Wicketコンポーネントを適用しているタグであれば,どこでも同じ方法で部分更新することができます。
ページの部分更新はWicketの機能の1つであり,そのために裏側でAjaxを使っている,という意味が伝わるでしょうか。Wicketでは,部分更新したければそのコンポーネントをAjaxRequestTargetに追加するだけでよいのです。あとは,Wicketが裏側で処理をしてくれます。極端に言えば,裏でAjaxが使われていることを知らないとしても,部分更新を行うことができます。
これがWicketの組み込みAjax機能です。
リプライリンクをAjax化する
ページを部分更新する方法が分かりました。早速,TwitterアプリケーションをAjax化してみましょう。まずは,リプライリンクをクリックするとページ全体が再描画されてしまう点を改善しましょう。
サンプルプログラム内にある,jp.gihyo.wicket.page.ajax.AjaxTimelineクラスを見てください。前回作成したPagingTimelineクラスを改良したものです。これから説明するプログラムは,すべてこのクラスに記述されています。
サンプルプログラムでは,replyLinkコンポーネントの次のようにAjaxLinkとして定義し直しました。
リスト3 replyLinkをAjaxLink化した例
item.add(new AjaxLink<Void>("replyLink") {
@Override
public void onClick(AjaxRequestTarget target) {
String targetScreenName = status.getUser().getScreenName();
form.insertText(target, "@" + targetScreenName + " ");
}
});
PagingTimelineクラスまでの例では,formのinsertText()メソッドの引数は1つだけでした。今回は引数は2つになり,第1引数としてAjaxRequestTargetを渡しています。実際のフォーム更新処理はinsertText()メソッド内で行われています。
リスト4 入力テキスト欄だけを更新する例
public void insertText(String text) {
updateText.setModelObject(text);
}
public void insertText(AjaxRequestTarget target, String text) {
insertText(text);
target.addComponent(updateText);
target.focusComponent(updateText);
}
引数が2つの新しいinsertText()メソッドは,まず従来の引数が1つのinsertText()メソッドを呼び出すことで,モデルを更新します。しかし今回は,単にモデルを更新してもページは再描画されません。そこで,テキスト入力欄を表すコンポーネントupdateTextをAjaxRequestTargetに追加することで,入力欄だけを再描画するように指定しました。
さらに,リプライリンクをクリックする場合,ユーザはすぐに入力を行うでしょうから,AjaxRequestTargetクラスのfocusComponent()メソッドを使って,入力フォーカスをupdateTextコンポーネントに移動しています。Wicketではフォーカス制御もこのように簡単に,コンポーネントで指定して行うことができるのです。
リプライリンクをAjax化して入力欄だけを部分更新する手順は,これだけです。Wicketの部分更新機能を使うことで,JavaScriptを意識することなく,ただ「このコンポーネントを再描画してください」と指令することで部分更新が行われるのです。ページの部分更新は,Wicketの標準機能の1つなのです。
Java, フレームワーク, wicket, JavaScript, Ajax


