本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

XForms と Ajax を使って autosuggest フォーム・フィールドを作成する

Michael Galpin, Developer, Vitria Technology
Michael Galpin は、1998年からプロとして Web アプリケーションの開発に取り組んでいます。彼は、カリフォルニア州サンノゼにある eBay のソフトウェア・エンジニアです。California Institute of Technology で数学の学位を取得しました。

概要: Ajax によって Web アプリケーション開発に革命的な変化が起こりました。かつては新鮮で派手な技術であったものが、今やどこででも見られるものになりました。その結果エンド・ユーザーは、Web アプリケーションとの何らかの対話動作が、「更新をしなくても」(つまり Ajax を使って) 行われることを期待するようになりつつあります。しかしユーザーにとっては Ajax が普通のものになりましたが、それに対応するクライアント・サイドの技術はまだ確立されていません。(クロス・ブラウザーの問題を隠すことで) Ajax を簡単に使えるようにする Ajax フレームワークは数多くありますが、Ajax 対応の Web アプリケーションを作成する作業は、控え目に言っても簡単ではないことは変わっていません。XForms は多くの利点を持つ標準化された技術であり、Ajax を補完することができます。この記事では、autosuggest フィールドを実装しながら、Ajax と XForms を合わせて使うことによる利点について解説します。

日付:  2007年 7月 10日
レベル:  中級 この記事の原文:  英語
アクティビティー: 2847 ビュー
お気軽にご意見・ご感想をお寄せください: 


前提

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 でテストされています。


Suggest の定義

この記事では、autosuggest フォーム・フィールドを作成します。これは Ajax のプロトタイプ機能です。この機能を最初に一般化したアプリケーションは Google Suggest です。Google Suggest では、検索対象のキーワードをユーザーが入力していくと、それに合わせてアプリケーションが (検索頻度に基づいて) 検索対象の言葉を提案します。ユーザーは検索対象の言葉をすべて入力せずにすむため、これはユーザーにとって便利な上、データ入力の間違いを防ぐ上でも効果があります。この技術は今や Web アプリケーションで広く使われています。それでは、この機能が、Ajax を使って通常どのように実装されているかを調べてみましょう。


Ajax で Suggest を作成する

Ajax 機能に関する限り、autosuggest フィールドは非常に簡単です。フォーム・フィールドにデータを入力するユーザーのイベントを、JavaScript を使ってキャプチャーします。これまでに入力されたものに基づく提案を要求するために、サーバーに対して非同期リクエストを行います。するとサーバーは提案を送り返します。次に JavaScript ハンドラーはこれらの提案を処理し、そして HTML の DOM を修正して提案を表示し、エンド・ユーザーが提案を選択できるようにします。では、Ajax ベースの autosuggest フィールドを作成する上での、これらの各手順を見てみましょう。まず、autosuggest フィールドの HTML から始めます。


HTML の autosuggest フィールド

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 に提案を入力します。では、これらの提案をどのように要求するのかを見てみましょう。


リクエストを送信するための JavaScript

上のセクションでは、(ユーザーがテキスト・ボックスに文字を入力すると) テキスト・ボックスは 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 GETroot パラメーターになります。最後に、レスポンス・ハンドラーを JavaScript 関数 handleSuggestions に設定していることに注目してください。これについては後で説明します。今度は、このリクエストをサーバーがどう処理するのかを見てみましょう。


提案を作成するための Java

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 を見てみましょう。


レスポンスを処理するための 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 によって単純になる様子を見てみましょう。


XForms と Ajax で suggest を作成する

Ajax の 2 つの重要な要素として、ユーザーがブラウザーで見ているページを変更せずにサーバーにリクエストを行える点と、サーバーから送り返される XML データを取得できる点があげられます。前者の特徴は非同期 (asynchronous) リクエストとして表現でき、これが Ajax の「A」にあたります。また後者の特徴から、Ajax に「X」が付いています。通常使われるのは XMLHttpRequest オブジェクトですが、これ以外に方法がないわけではありません。XForms でも同じことを実現でき、XForms を使えば Ajax に「A」と「X」を付けられるのです。


XForms の理論的枠組み

XForms は、Web アプリケーションのビューからモデルを分離しています。データは分離されているため、容易に変更することができます。すべてのデータとアクション (サーバーへのリクエスト) は、MVC (Model View Control) アーキテクチャーで一般的に見られるように、モデルの一部です。この autosuggest アプリケーションでは、モデルの一部として、リクエスト・データ (サーバーに送信されるルート) があり、レスポンス・データ (サーバーからの提案) があり、そしてアクション (サーバーに対する HTTP リクエスト) があります。ではこうした、モデルのさまざまな部分を見てみましょう。


XForms を設定する: リクエスト・モデル

サーバーへのリクエストに使おうとしているデータが、このモデルの最初の部分です。これは非常に単純で、単に root パラメーターが必要なだけです (リスト 6)。


リスト 6. XForms モデル: リクエスト・データ
                                
     <xf:instance id="xfsuggestQuery">
          <query xmlns=">
               <root/>
          </query>
     </xf:instance>

これは、サーバーに送信するための root パラメーターを保存するための単純な XML 文書です。後ほど、これがビューにどうバインドされるかを調べます。今度は、モデルの中にレスポンス・データを取り入れるための方法を見てみましょう。



XForms を設定する: レスポンス・モデル

レスポンス・データのためのデータは、もう少し複雑です。すべてを Ajax で行った場合に使用した構造は適切な形式の XML であり、XForms と完全に互換性があったので、この同じ構造を使います。しかしアプリケーションが起動した時には何も提案がないため、今必要なものは提案を保存するためのプレースホルダーのみです (リスト 7)。


リスト 7. XForms モデル: レスポンス・データ
                                
     <xf:instance id="xfsuggestResults">
          <suggestions xmlns="/>
     </xf:instance>

サーバーからレスポンスを取得したら、suggestions ノードを完全に置き換える必要があります。今は suggestions を空のままにしておきます。モデルに必要なデータは、これだけです。今度はモデルに対するアクションが必要です。


XForms を設定する: サーバー・アクション

データはモデルの一部ですが、データに対して行うアクションも同じくらい重要です。この場合のアクションは、サーバーに対する 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 のビュー: 入力

この 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 フィールドに対する提案がサーバーにリクエストされます。では、サーバー・コードがこれをどう処理するのかを見てみましょう。


サーバー・サイドの Java を活用する

では、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 codexforms_suggest_source.zip376KBHTTP

ダウンロード形式について


参考文献

学ぶために

製品や技術を入手するために

議論するために

著者について

Michael Galpin は、1998年からプロとして Web アプリケーションの開発に取り組んでいます。彼は、カリフォルニア州サンノゼにある eBay のソフトウェア・エンジニアです。California Institute of Technology で数学の学位を取得しました。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=246190
ArticleTitle=XForms と Ajax を使って autosuggest フォーム・フィールドを作成する
publish-date=07102007
author1-email=mike.sr@gmail.com
author1-email-cc=ruterbo@us.ibm.com

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。