XForms は標準化された技術ですが、大部分のブラウザーでは、まだデフォルトでは実装されていません。この記事では Mozilla XForms のアドオンを使います。このアドオンは、Firefox や Seamonkey、Flock など Mozilla Gecko ベースのブラウザーであれば、どれにも使用できます。ここで使用しているすべての XForms コードは標準的なものです。そのため、標準に準拠した XForms の実装ならばどれも同じはずですが、ここでは Mozilla Firefox 2.0 にインストールした Mozilla XForms add-on バージョン 0.8 でしかテストしていません。同様に、JavaScript コードも Firefox でしかテストしていません。
すべての Ajax アプリケーションは、バック・エンド・サーバーからデータをリクエストする必要があります。この記事で使用したサーバー技術は Java™ 技術です。従って、Servlet 2.4 仕様を実装した Java Web コンテナーと共に、Java 1.5 以上が必要です。このアプリケーションは Apache Tomcat 5.5 でテストされています。
この記事では、autosuggest フォーム・フィールドを作成します。これは Ajax のプロトタイプ機能です。この機能を最初に一般化したアプリケーションは Google Suggest です。Google Suggest では、検索対象のキーワードをユーザーが入力していくと、それに合わせてアプリケーションが (検索頻度に基づいて) 検索対象の言葉を提案します。ユーザーは検索対象の言葉をすべて入力せずにすむため、これはユーザーにとって便利な上、データ入力の間違いを防ぐ上でも効果があります。この技術は今や Web アプリケーションで広く使われています。それでは、この機能が、Ajax を使って通常どのように実装されているかを調べてみましょう。
Ajax 機能に関する限り、autosuggest フィールドは非常に簡単です。フォーム・フィールドにデータを入力するユーザーのイベントを、JavaScript を使ってキャプチャーします。これまでに入力されたものに基づく提案を要求するために、サーバーに対して非同期リクエストを行います。するとサーバーは提案を送り返します。次に JavaScript ハンドラーはこれらの提案を処理し、そして HTML の DOM を修正して提案を表示し、エンド・ユーザーが提案を選択できるようにします。では、Ajax ベースの autosuggest フィールドを作成する上での、これらの各手順を見てみましょう。まず、autosuggest フィールドの HTML から始めます。
autosuggest フィールドのための HTML は非常に単純です。これをリスト 1 に示します。
リスト 1. autosuggest フィールドの HTML
<input type="text" id="tBox" onkeyup="suggest();" autocomplete="off"/>
<div id="suggest"></div> |
これは非常に単純なものです。単純なテキスト・フィールドがありますが、その属性が非常に重要です。まず、onkeyup イベントが起動されたら必ず suggest という JavaScript 関数が呼び出されるように、onkeyup 属性を使って宣言します。この関数については次のセクションで解説します。autocomplete 属性が Off に設定されていることにも注目してください。これは、ブラウザーがフォーム・フィールドに事前入力してしまうのを防止するためのものです。そして最後に、空の
div を作成していることに注目してください。この div に提案を入力します。では、これらの提案をどのように要求するのかを見てみましょう。
上のセクションでは、(ユーザーがテキスト・ボックスに文字を入力すると) テキスト・ボックスは suggest 関数が呼び出されるように要求することがわかりました。この関数をリスト 2 に示します。
リスト 2. suggest JavaScript 関数
//This doesn't work in IE 6
var suggestReq = new XMLHttpRequest();
function suggest() {
if (suggestReq.readyState == 4 || suggestReq.readyState == 0) {
var str = escape(document.getElementById('tBox').value);
suggestReq.open("GET", '/xfsuggest/suggest?root=' + str, true);
suggestReq.onreadystatechange = handleSuggestions;
suggestReq.send(null);
}
} |
suggest 関数は、XMLHttpRequest オブジェクトを使って /xfsuggest/suggest という URL にリクエストを行います。リスト 2 のコードは、Internet
Explorer 6 でも、それ以前の Internet Explorer でも動作しません。Internet Explorer 6 では、ブラウザーをスニッフィングしてから新しい
ActiveXObject("Microsoft.XMLHTTP") を呼び出して XMLHttpRequest オブジェクトを取得する必要があります。XMLHttpRequestオブジェクトを取得できたら、それまでにユーザーが autosuggest フィールドに入力したものを、JavaScript を使って取得します。これがサーバーへの
HTTP GET の root パラメーターになります。最後に、レスポンス・ハンドラーを JavaScript 関数 handleSuggestions に設定していることに注目してください。これについては後で説明します。今度は、このリクエストをサーバーがどう処理するのかを見てみましょう。
Ajax (そして XForms) のようなクライアント・サイド技術の素晴らしさの 1 つは、これらの技術がサーバー・サイドの技術に依存しない点です。PHP であろうと Ruby であろうと、好きなものを使えます。ここでは Java サーブレットを使います。このサーブレットのコードをリスト 3 に示します。
リスト 3. Suggest サーブレット
package org.developerworks.xfsuggest;
import java.io.IOException;
import java.util.*;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class SuggestServlet extends HttpServlet {
private final int listSize = 15;
private final Random gen = new Random(Calendar.getInstance().getTimeInMillis());
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
String root = request.getParameter("root");
List<String> suggestions = this.getSuggestions(root);
request.setAttribute("suggestions", suggestions);
request.getRequestDispatcher("/suggestions.jsp").forward(request, response);
}
private List<String> getSuggestions(String str){
if (str.length() >= listSize){
return Collections.emptyList();
}
int size = listSize - str.length();
List<String> suggestions = new ArrayList<String>();
for (int i = 0; i<size; i++){
StringBuilder sb = new StringBuilder(str);
for (int j = 0; j < size; j++){
char ch = (char) ('a' + gen.nextInt(26));
sb.append(ch);
}
suggestions.add(sb.toString());
}
return suggestions;
}
} |
このサーブレットは単純にルート・リクエスト・パラメーターを取得し、そして getSuggestions() メソッドを呼び出して提案のリストを取得します。リスト 3 の実装は、提案のためのストリングのランダムなリストを作成します。この疑似実装は、ルートが長い場合には作成する提案を少なくしており、autosuggest
フィールドへの入力が増えるにつれて候補が絞り込まれる動作を真似ています。このリストは次にリクエスト・オブジェクトの中に置かれます。そしてサーブレットは出力描画用の
JSP に転送します。これをリスト 4 に示します。
リスト 4. suggestions.jsp
<?xml version="1.0" encoding="ISO-8859-1"?>
<%@ page language="java" contentType="text/xml; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<suggestions>
<c:forEach items="${suggestions}" var="str">
<word>${str}</word>
</c:forEach>
</suggestions> |
この JSP は、クライアントに送り返すための XML 文書を作成します。JSP はこれを、サーブレットが作成した提案リストを単純に繰り返すことで行います。今度は、このデータを処理するためのクライアント・サイドの JavaScript を見てみましょう。
先ほど XMLHttpRequest オブジェクトを使ってサーバーを呼び出した際に、レスポンス・ハンドラーを登録しました。従って、リスト 4 に示すようにサーバーが XML に応答すると、この JavaScript ハンドラーが呼び出されます。この種の非同期フローは、Ajax プログラミングで最も一般的な落とし穴です。では、サーバーからのレスポンスを処理する
handleSuggestions 関数を見てみましょう (リスト 5)。
リスト 5. レスポンス・ハンドラー: handleSuggestions JavaScript 関数
function handleSuggestions() {
if (suggestReq.readyState == 4) {
var ss = document.getElementById('suggest')
ss.innerHTML = '';
var xml = suggestReq.responseXML;
var root = xml.getElementsByTagName('suggestions').item(0);
var suggestions = new Array();
var cnt = 0;
for (var i=0; i < root.childNodes.length; i++){
var node = root.childNodes.item(i);
if (node.childNodes.length > 0){
suggestions[cnt++] = node.childNodes.item(0).data;
}
}
for(i=0; i < suggestions.length; i++) {
var val = suggestions[i];
if (val.length > 0){
var suggest = '<div
onmouseover="javascript:suggestOver(this);" ';
suggest += 'onmouseout="javascript:suggestOut(this);" ';
suggest += 'onclick="javascript:setValue(this.innerHTML);" ';
suggest += 'class="suggest_link">' + val + '</div>';
ss.innerHTML += suggest;
}
}
}
} |
この関数には主要な部分が 2 つあります。まず、responseXML 文書をウォークすることによって XML を処理し、そして提案を取得します。これらの提案は suggestions 配列に保存されます。次にコードは、ユーザーが選択するための提案リストを動的に作成し、そのリストを
HTML で作成された suggest div の中に置きます。図 1 は autosuggest フィールドの様子を示しています。
図 1. Ajax を使った autosuggest
「提案の効果」を作成するために、他にもいくつかの CSS と JavaScript が使われています。これらを見るためには、完全なソース・コードを参照してください。今度は、これが今度は、これが XForms によって単純になる様子を見てみましょう。
Ajax の 2 つの重要な要素として、ユーザーがブラウザーで見ているページを変更せずにサーバーにリクエストを行える点と、サーバーから送り返される
XML データを取得できる点があげられます。前者の特徴は非同期 (asynchronous) リクエストとして表現でき、これが Ajax の「A」にあたります。また後者の特徴から、Ajax
に「X」が付いています。通常使われるのは XMLHttpRequest オブジェクトですが、これ以外に方法がないわけではありません。XForms でも同じことを実現でき、XForms を使えば Ajax に「A」と「X」を付けられるのです。
XForms は、Web アプリケーションのビューからモデルを分離しています。データは分離されているため、容易に変更することができます。すべてのデータとアクション (サーバーへのリクエスト) は、MVC (Model View Control) アーキテクチャーで一般的に見られるように、モデルの一部です。この autosuggest アプリケーションでは、モデルの一部として、リクエスト・データ (サーバーに送信されるルート) があり、レスポンス・データ (サーバーからの提案) があり、そしてアクション (サーバーに対する HTTP リクエスト) があります。ではこうした、モデルのさまざまな部分を見てみましょう。
サーバーへのリクエストに使おうとしているデータが、このモデルの最初の部分です。これは非常に単純で、単に root パラメーターが必要なだけです (リスト 6)。
リスト 6. XForms モデル: リクエスト・データ
<xf:instance id="xfsuggestQuery">
<query xmlns=">
<root/>
</query>
</xf:instance> |
これは、サーバーに送信するための root パラメーターを保存するための単純な XML 文書です。後ほど、これがビューにどうバインドされるかを調べます。今度は、モデルの中にレスポンス・データを取り入れるための方法を見てみましょう。
レスポンス・データのためのデータは、もう少し複雑です。すべてを Ajax で行った場合に使用した構造は適切な形式の XML であり、XForms と完全に互換性があったので、この同じ構造を使います。しかしアプリケーションが起動した時には何も提案がないため、今必要なものは提案を保存するためのプレースホルダーのみです (リスト 7)。
リスト 7. XForms モデル: レスポンス・データ
<xf:instance id="xfsuggestResults">
<suggestions xmlns="/>
</xf:instance> |
サーバーからレスポンスを取得したら、suggestions ノードを完全に置き換える必要があります。今は suggestions を空のままにしておきます。モデルに必要なデータは、これだけです。今度はモデルに対するアクションが必要です。
データはモデルの一部ですが、データに対して行うアクションも同じくらい重要です。この場合のアクションは、サーバーに対する HTTP リクエストです。リスト 8 は、XForms の submission ノードを使うことで、これがいかに容易に行えるかを示しています。
リスト 8. XForms モデル: 送信
<xf:submission id="get_suggest" action="/xfsuggest/suggest"
method="get" separator="&" ref="instance('xfsuggestQuery')"
replace="instance" instance="xfsuggestResults"/> |
submission に対するアクション (action) は、行おうとしている HTTP リクエストの URL です。ref パラメーターは、何を送信すべきかを submission に伝えます。この場合は、xfsuggestQuery データ・オブジェクトからデータを引き出すように指示しています。これはリスト 6 で定義されたリクエスト・データです。submission に対するメソッド (method) は get であり、区切り文字 (separator) はアンパーサンド です。これは HTTP リクエストへのデータの追加方法を XForms に伝えています。これによって
root というパラメーターが作成されます。その理由は、root というパラメーターが xfsuggestQuery というクエリー・ノードの要素であり、このパラメーターの値は
XML データの「root」ノードのテキスト・ノードの子の値だからです。複数のパラメーターがある場合には、アンパーサンドを使って区切られた適切な形式の
HTTP GET が作成されます。これで XForms モデルを定義できたので、あとはこのモデルのビューを定義するだけです。
この XForms でのビューは非常に単純であり、単なるテキスト・フィールドにすぎません。ただしこのテキスト・フィールドは Ajax 風の autosuggest 機能を持つことになるため、通常のテキスト・フィールドではありません。この宣言をリスト 9 に示します。
リスト 9. XForms のビュー
<xf:input ref="instance('xfsuggestQuery')/root" incremental="true" id="tBox">
<xf:action ev:event="xforms-value-changed">
<xf:send submission="get_suggest" />
</xf:action>
</xf:input> |
ここで何が行われているのかを見てみましょう。まず、入力フィールドを定義します。これを、ref 属性を使ってモデルにバインドします。これは XForms に対して、どのような値が入力フィールドに入力された場合にも、その値を xfsuggestQuery という XML 文書の「root」ノードにも入力することを指示しています。次に、入力に対して定義されるアクションがあります。このアクションは、モデルの中で作成された
submission に対して「send」を呼び出します。これらはどれも XForms では非常に一般的なものであり、XForms を扱った経験のある開発者にはおなじみのはずです。
ただし、あまり一般的ではない XForms 機能もいくつか使われています。入力には incremental という属性があり、true に設定されています。この属性は、入力がインクリメンタルに変更されるとイベントを起動します。実際これは、リスト 1 の HTML 入力の中で示したonkeyup 型のイベントよりも優れています。これによって XForms はイベントの起動を制限しやすくなり、ユーザーが高速で入力した場合にもイベントの起動を制限することができます。多くの
Ajax アプリケーションでは、ここで示すように XForms ならば苦労なく実現できることを、独自の制限機構を追加して実現しなければならないことがよくあります。
ここで定義したアクションは、「xforms-value-changed」に設定されたイベント属性を持っています。これによってアクションは、入力によって起動されるインクリメンタルなイベントをリッスンするようになります。そして、これらのイベントが起動されるたびに送信アクションが呼び出され、それによって autosuggest フィールドに対する提案がサーバーにリクエストされます。では、サーバー・コードがこれをどう処理するのかを見てみましょう。
では、XForms からのこうしたリクエストを処理するために、サーバーをどう変更するのでしょう。実は変更する必要がありません。サーバーは既に Ajax に対して XML を送り返しており、XForms にはこれで十分なのです。これは、XForms が Ajax を補完する、さまざまな方法の 1 つを示しています。一方の技術で使われている同じサーバー・サイドのサービスを、もう一方の技術にも使うことができます。あとはサーバーからのレスポンスをクライアント上で処理すればよいだけです。
この Ajax アプリケーションでは、サーバーからの responseXML を処理するために、そして HTML DOM を修正して提案を表示するために、JavaScript 関数を使いました。XForms を使う場合には、データに直接バインドすることで、もっと単純に行うことができます。つまりデータがサーバーに送信されると、ビューは自らを自動的に更新するのです。そこで、空の suggest div を持つ代わりに、バインドされるデータをいくつか追加します (リスト 10)。
リスト 10. バインドされた XForms データを使って結果を表示する
<div id="suggest">
<xf:repeat id="results" nodeset="instance('xfsuggestResults')/word">
<div onmouseover="javascript:suggestOver(this);"
onmouseout="javascript:suggestOut(this);"
onclick="javascript:setValue(this.innerHTML);" class="suggest_link">
<xf:output ref="."/>
</div>
</xf:repeat>
</div> |
ここで行っているのは、xfsugggestResults 文書の「word」ノードにバインドすることだけです。最初は当然 word は存在していませんが、いったんサーバーから結果を取得すれば word
が存在するようになります。そして「word」ノードができたら、それらのノードに対して繰り返しを行い、その word が中に入った div を作成します。この
div は、すべてを Ajax で行った場合に JavaScript で動的に作成された div と、まったく同じです。この div が参照する
suggestOver と suggestOut という JavaScript 関数は変わりません。変更する必要があるのは、setValue 関数のみです。この関数は、HTML の入力フィールドのデータを単に変更するのではなく、XForms データを処理する必要があります。リスト
11 の setValue を見てください。
リスト 11. 新しい setValue 関数
function setValue(value) {
var model = document.getElementById("xfsuggestModel");
var instance = model.getInstanceDocument("xfsuggestQuery");
var queryElement = model.getElementsByTagName("query")[0];
var rootElement = queryElement.getElementsByTagName("root")[0];
if (rootElement.childNodes.length == 0){
var textNode = document.createTextNode(value);
rootElement.appendChild(textNode);
} else {
rootElement.firstChild.nodeValue = value;
}
model.rebuild();
model.refresh();
document.getElementById('suggest').innerHTML = '';
} |
この関数では、XForms モデルにアクセスし、xfsuggestQuery インスタンスまでウォークし、次にその root 要素までウォークし、そしてそこにテキスト・ノードを追加するか、あるいはその値が既に存在している場合には、その値を変更します。この方法は、xfsuggestResults データに直接アクセスする場合にも使うことができ、先ほどと同じように提案を表示することができます。JavaScript と XForms が非常にうまく連係動作しています。
この記事では、XForms を使うことで Ajax を補完できることを学びました。XForms は Ajax と同じ理論的枠組みを数多く使用していますが、単純化と最適化をいくつか行っています。XForms を使った方がサーバーとのデータの送受信が容易になり、またサーバーから「より洗練された」イベントを取得することができます。また XForms は JavaScript が通常実装する数多くの「ボイラープレート」機能を提供しているため、必要な JavaScript を単純化することもできます。結論として、XForms に Ajax の「A」と「X」を行わせるとメリットがあることが多いのです。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Article sample code | xforms_suggest_source.zip | 376KB | HTTP |
学ぶために
- XForms の基本への入門として、「XForms 入門、第 1 回: フォームのための新しい Web 標準」(Chris Herborth 著、developerWorks、2006年9月) と「XForms 入門、第 2 回: フォーム、モデル、コントロール、そして送信アクション」 (Chris Herborth 著、developerWorks、2006年9月)、そして「XForms 入門、第 3 回: アクションとイベントを使う」(Chris Herborth 著、developerWorks、2006年9月) を読んでください。
- XForms と Ajax とを組み合わせて使う方法を学ぶために、記事「XForms のヒント: Ajax と XForms とを組み合わせる」(Nicholas Chase 著、developerWorks、2006年10月) を読んでください。
- JavaScript と XForms とを組み合わせて使う方法を学ぶために、記事「XForms のヒント: XForms フォームから JavaScript をコールする」(Nicholas Chase 著、developerWorks、2007年1月) を読んでください。
- この記事で使われている repeat コマンドの力を最大限利用するために、developerWorks の記事 「XForms の repeat を最大限に利用する」 (Jan J. Kratky と Steve K. Speicher の共著、developerWorks、2006年11月) を読んでください。
- より速く XForms を作成する方法を学ぶために、「Develop forms using the Visual XForms Designer」 (Jan J. Kratky と Keith Wells、Kevin E. Kelly の共著、developerWorks、2006年6月) で Visual XForms Designer について読んでください。
- XForms で Ajax を単純化する方法を学ぶために、Kurt Cagle のブログ「Why XForms Matter, Revisited」を見てください。
- IBM のdeveloperWorks XML ゾーンで XForms を学んでください。
- Ajax に関する最高の情報源として、developerWorks のAjax resource centerがあります。
-
XForms の方向性を学んでください。
- 次世代のフォームを作成する方法の基本を学ぶために、「XForms の基本」 (Nicholas Chase 著、developerWorks、2006年10月) を読んでください。
-
IBM の XForms コミュニティーを訪れてください。
- Mozilla 用のXForms 拡張機能をダウンロードしてください。
- XML および関連技術において IBM 認証開発者になる方法については、IBM XML certificationを参照してください。
- developerWorks の XML ゾーンはXML の技術ライブラリーとして、広範な話題を網羅した技術記事やヒント、チュートリアル、技術標準、IBM レッドブックなどを用意しています。
-
developerWorks technical events and webcastsで最新情報を入手してください。
-
developerWorks の XML ゾーンで XML を学んでください。
製品や技術を入手するために
-
XForms Recommendationは W3C によって維持管理されています。
議論するために
-
ディスカッション・フォーラムに参加してください。