Web 2.0 アプリケーションの中で最も魅力的なものとしてよく頭に浮かぶのは、YouTube の動画や卓越したスクロール機能を持つ Google マップの地図、それに撮影場所を地図に示すことができる Flickr の機能などです。そんなサイトのなかで、ささやかな HTML フォームは見過ごされがちですが、HTML フォームはAjax 技術の普及に伴って大きな変容を遂げています。
この記事では、フォームを Ajax コードで拡張する際によくあるユーザー・エクスペリエンスの問題を、Prototype.js JavaScript ライブラリーを使って解決する方法を紹介します。
まずはごく単純な例から始めます。登録フォームに複数のフィールドがある場合に、通常の Web フォームの POST を使う代わりに Ajax を使って送信するとします。この単純なフォームのページをリスト 1 に記載します。
リスト 1. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="myform">
<table>
<tr><td>First</td><td><input type="text" name="first"></td></tr>
<tr><td>Last</td><td><input type="text" name="last"></td></tr>
<tr><td>Email</td><td><input type="text" name="email"></td></tr>
</table>
<input type="button" onclick="dosubmit()" value="Submit">
</form>
<div id="result" style="padding:5px;">
</div>
<script>
function dosubmit( ) {
new Ajax.Updater( 'result', 'add.php', { method: 'post',
parameters: $('myform').serialize() } );
$('myform').reset();
}
</script>
</body>
</html>
|
ファイルの先頭で組み込まれているのは、Ajax の処理をすべて自動的に行ってくれる prototype.js JavaScript ファイルです。その後に、first、last、email という3 つのフィールドを持つ従来の HTML フォームが続き、フィールドの下にはフォームを送信するボタンがあります。このボタンが送信を行うために使用するのは、dosubmit() JavaScript 関数です。
dosubmit() 関数は、Ajax.Updater クラスを使用してデータを add.php スクリプトに送ります。この関数呼び出しにはオプションを追加します。上記の例では、送信メソッドを post に設定し、フォームからのパラメーターを追加する手段としてフォームの serialize() メソッドを使っています。serialize() メソッドは標準の JavaScript コードではなく、JavaScript ライブラリーによって提供されるメソッドです。
Ajax.Updater 呼び出しで最初の項目となっているのは、add.php スクリプトが返す HTML を受け取る <div> タグの ID です。ボタンのクリックによって何が行われたかをユーザーに通知するには、これが最も簡単な方法です。
Ajax.Updater を呼び出す際に渡す 1 番目の引数は <div> タグの ID で、このタグが add.php スクリプトが返す HTML を受け取ります。ボタンのクリックによって何が行われたかをユーザーに通知するには、これが最も簡単な方法です。
リスト 2 に、add.php スクリプトを記載します。
リスト 2. add.php
Thanks <?php echo( $_POST['first'] ) ?> <?php echo( $_POST['last'] ) ?>!
|
上記のスクリプトは、フォームから送られた内容をエコー・バックするだけに過ぎません。実際には、データベースにレコードを追加することになるでしょうが、その類のビジネス・ロジックの作業は読者の皆さんにおまかせします。
このフォームがブラウザーにロードされると、図 1 のような表示になります。
図 1. 単純な Ajax フォーム
Submit ボタンをクリックするとフォームのデータが add.php ページに送信され、そのページから返された HTML が Submit ボタンのすぐ下に表示されます (図 2 を参照)。
図 2. 送信した後のレスポンス
Ajax を使ったフォームのもう 1 つのバリエーションとしては、自動入力フォームがあります。このフォームでは、キーとなるいくつかのフィールドの値に応じてフィールドの値が更新されます。例えば、カスタマーの ID を入力してボタンをクリックすると、現在のカスタマーのレコードが他のフィールドに入力されるといった具合です。
このような自動入力フォームをリスト 3 に記載します。
リスト 3. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="myform">
<table>
<tr><td>ID</td><td><input
type="text" name="id"></td></tr>
<tr><td>First</td><td><input type="text"
name="first" id="elFirst"></td></tr>
<tr><td>Last</td><td><input type="text"
name="last" id="elLast"></td></tr>
<tr><td>Email</td><td><input type="text"
name="email" id="elEmail"></td></tr>
</table>
<input type="button" onclick="dofill()" value="Fill Fields">
</form>
<script>
function dofill( ) {
new Ajax.Updater( 'result', 'getdata.php',
{ method: 'post', parameters: $('myform').serialize(),
onSuccess: function( transport ) {
$('elFirst').value = transport.responseXML.getElementsByTagName('first')
[0].firstChild.nodeValue;
$('elLast').value = transport.responseXML.getElementsByTagName('last')
[0].firstChild.nodeValue;
$('elEmail').value = transport.responseXML.getElementsByTagName('email')
[0].firstChild.nodeValue;
} } );
}
</script>
</body>
</html>
|
上記では、リスト 1 のフォームに ID というフィールドを追加しました。ここに、カスタマーの ID を入力します。新しく追加した dofill() 関数が getdata.php ページを呼び出すと、このページから、指定されたカスタマー ID に対応する名前、苗字、E メール・アドレスが含まれる XML コードが返されます。
Ajax.Updater 呼び出しに送信する onSuccess ハンドラーは、すべてのブラウザーで使用可能なネイティブ DOM (Document Object Model) 関数を使用してダウンロードされた XML データを分解します。そして、elFirst、elLast、elEmail といった <input> 項目の値を、XML の中に入れられて返された値にします。
getdata.php ページはリスト 4 のとおりです。
リスト 4. getdata.php
<?php
header( "content-type: text/xml" );
$first = ' ';
$last = ' ';
$email = ' ';
if ( $_POST['id'] == '1' )
{
$first = 'Jack';
$last = 'Herrington';
$email = 'jherr@pobox.com';
}
?>
<data>
<first><?php echo( $first ) ?></first>
<last><?php echo( $last ) ?></last>
<email><?php echo( $email ) ?></email>
</data>
|
通常の場合、データベースを呼び出して、指定されたレコードの名前、苗字、E メール・アドレスを取得するのは単なるスタブ・メソッドに過ぎません。
このページは、最初に表示した時点では図 3 のようになっています。
図 3. 入力フォーム
ID フィールドに 1 と入力してから Fill Fields をクリックすると、getdata.php ページから名前、苗字、E メール・アドレスが取得され、それぞれに対応するフィールドが更新されます。すると、図 4 のような表示になります。
図 4. ID に応じてスクリプトによって入力されたフィールド
次の Ajax のマジックでは、その場で更新される ToDO リストを作成します。
Ruby on Rails のデモとしてよく使われるものに、その場で更新される ToDo リストがあります。これは、ページの先頭に項目のリストがあり、その下にテキスト・ボックスがあるというものです。テキスト・ボックスに何らかのテキストを入力して Submit をクリックすると、ページはそのままでページの先頭のリストが新しい項目で更新されます。テキストを入力したテキスト・ボックスはリセットされるため、簡単に項目を追加していくことができます。
実際これは非常に難しい芸当なので、PHP で同じことを実現する方法を披露することにします。リスト 5 の ToDo リストのページを見てください。
リスト 5. index.php
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<div id="result" style="padding:5px;">
<?php
$fh = fopen( 'list.txt', 'r' );
while( $str = fgets( $fh ) ) {
?>
<?php echo( $str ); ?><br/>
<?php
}
?>
</div>
<form id="myform">
<input type="text" name="todo">
</form>
<input type="button" onclick="dosubmit()" value="Submit">
<script>
function dosubmit( ) {
new Ajax.Updater( 'result', 'add.php',
{ method: 'post', parameters: $('myform').serialize() } );
$('myform').reset();
}
</script>
</body>
</html>
|
ToDo リストをデータベースに保持する代わりに、ここでは list.txt というフラット・テキスト・ファイルに項目を 1 行あたり 1 項目ずつ保存します。したがって、このファイルを開き、ファイル内の各行を result という ID を持つ <div> タグに読み込むだけで、ページの先頭にリストが表示されます。
リストの下には、ToDo 項目をテキストで入力するフォームがあります。フォームの下にあるボタンは、dosubmit() JavaScript 関数を呼び出します。この JavaScript 関数は Ajax.Updater クラスを使って add.php ページを呼び出すことにより、項目を追加し、その項目が追加された新しいリストを返します。
リスト 6 に、add.php スクリプトを記載します。
リスト 6. add.php
<?php
$total = '';
$fh = fopen( 'list.txt', 'r' );
while( $str = fgets( $fh ) ) {
?>
<?php echo( $str ); ?><br/>
<?php
$total .= $str;
}
if ( array_key_exists( 'todo', $_POST ) )
{
?>
<?php echo( $_POST['todo'] ); ?><br/>
<?php
$fh = fopen( 'list.txt', 'w' );
fwrite( $fh, $total."\n".$_POST['todo'] );
fclose( $fh );
}
?>
|
リスト 7 は、私個人のたわいのない ToDo 項目です。
リスト 7. list.txt
Get swim goggles
Practice swimming
Swim in race
|
このページは最初、図 5 のように表示されます。
図 5. ToDo 項目が追加される前の状態
このページに Finish in record time と入力して Submit をクリックすると、ページは更新されずに図 6 のような結果になります。
図 6. レコードが挿入された後のページ
もちろん、Rails の ToDo リストのデモにある優れた機能はこれだけではなく、Web 2.0 ならではの「感動的」要素は他にもあります。Rails を試したことのない Web エンジニアには是非一度試してみることをお勧めします。自分のプロジェクトで使用しないとしても、アプリケーションの構成方法、モデル・ビュー・コントローラー (MVC) 構造の適用方法、そしてデータベースの永続モデルの簡単さはまさに注目に値します。
ToDo リストの他に、次々と展開していくフィールドのリストも Web ではよく必要となります。私はこれを、expando リストと呼んでいます。
1 つのレコードに無数のキーワードを関連付けられるとしたらどうでしょう。このような場合に対処するには、キーワードをカンマで区切るという方法があります。あるいは、新しいキーワード・フィールドをその場で追加するためのボタンを用意して、ユーザーが好きな数だけキーワードを追加できるようにするというのも 1 つの方法です。リスト 8 では、後者の方法を使用しています。
リスト 8. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="myform">
<table id="keytable">
<tr><td>Keyword</td><td><input type="text"
name="keyword_1"></td></tr>
</table>
</form>
<input type="button" onclick="addkeyword()" value="Add Keyword">
<input type="button" onclick="dosubmit()" value="Submit">
<div id="result" style="padding:5px;">
</div>
<script>
var nextkeyid = 2;
function addkeyword()
{
var elTR = $('keytable').insertRow( -1 );
var elTitleTD = elTR.insertCell( -1 );
elTitleTD.appendChild( document.createTextNode( 'Keyword' ) );
var elInput = document.createElement( 'input' );
elInput.type = 'text';
elInput.name = 'keyword_'+nextkeyid;
nextkeyid++;
var elInputTD = elTR.insertCell( -1 );
elInputTD.appendChild( elInput );
}
function dosubmit( ) {
new Ajax.Updater( 'result', 'add.php',
{ method: 'post', parameters: $('myform').serialize() } );
}
</script>
</body>
</html>
|
この方法の本当のトリックは addkeyword() 関数にあります。この関数は insertRow と insertCell を使ってキーワードのテーブルに新しい行を作成した後、document.createElement を使ってキーワードを保持するための入力フィールドを新規に作成します。ユーザーが Submit をクリックすると Ajax.Updater コードが呼び出され、このコードによって add.php スクリプトが呼び出されます。すると、add.php スクリプトは単にフォームで指定されたキーワードのリストを返します。このスクリプトはリスト 9 のとおりです。
リスト 9. add.php
Post Result:<br/>
<?php var_export( $_POST ) ?>
|
ブラウザーでこのページにアクセスすると、始めは図 7 のように表示されます。
図 7. キーワードが 1 つしかないキーワード・フォーム
Add Keyword を何度かクリックして新規フィールドを追加して Submit をクリックすると、図 8 の画面になります。
図 8. キーワードを追加して Submit をクリックした後のキーワード・フォーム
この方法は、ユーザーが単一のレコードに電話番号、キーワード、アドレスなど、関連する複数の値を追加できるようにするには最適です。Ajax はまた、ログイン・フォームを実装するためにもよく使われます。
Ajax のログイン・フォームが特に優れている理由は、ログインが成功したかどうかをすぐにフィードバックできるだけでなく、その場でログインを実行できるためです。例えば、表示中の記事にコメントを送りたいけれども、ログインしていないとします。そんな場合、Ajax では記事を表示したままログインすることができるのです。クレデンシャルが受け付けられると、コメントを追加するための新しいフォームが表示されます。このプロセスは、ユーザーがログインに成功してからログイン・フローを調べ、そのユーザーが表示していた記事を探し出してリダイレクトするより遥かに簡単です。
リスト 10 は、Ajax ログインの単純なバージョンです。
リスト 10. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="logform">
User: <input type="text" name="user"><br/>
Password: <input type="password" name="password"><br/>
<input type="button" onclick="login()" value="Login">
</form>
<div id="noway" style="display:none;">
No way!
</div>
<script>
function login() {
new Ajax.Request( 'login.php',
{
method: 'post',
postBody: $('logform').serialize(),
onSuccess: function( transport ) {
if( transport.responseText.match( /\<ok\/\>/ ) )
window.location = 'home.html';
else
$('noway').style.display='block';
}
} );
}
</script>
</body>
</html>
|
User および Password フィールドはファイル先頭のフォームに含まれます。この 2 つのフィールドの下にある、ID が noway に設定された <div> タグはログインが受け入れられない場合に表示されます。login() JavaScript メソッドがログインを試行するために使用するのは Ajax.Request クラスです。login.php から返された XML が <ok /> であれば、フォームはユーザーをホーム・ページにリダイレクトし、そうでない場合は noway のテキストを表示します。
リスト 11 は、login.php のコードです。
リスト 11. login.php
<?php
header( 'Content-type: text/xml' );
if ( $_POST['user'] == 'jack' && $_POST['password'] == 'password' )
echo( "<ok/>" );
else
echo( "<bad/>" );
?>
|
この単純なコードは、ユーザーとパスワードをハード・コーディングされた値と照合し、一致していれば ok を返し、一致していなければ bad を返します。
念のため、このホーム・ページのコードをリスト 12 に記載します。
リスト 12. home.html
<html>
<body>
You are logged in and this is your home page.
</body>
</html>
|
このサンプル・コードをブラウザーから実行するために、ページを表示して無効なユーザー名とパスワードを入力し、Login をクリックします。その結果を図 9 に示します。
図 9. 無効なパスワードを入力した場合のログイン・ページ
今度はユーザー名とパスワードを正しい値に変更して Login をクリックします。すると、図 10 に示すホーム・ページにリダイレクトされます。
図 10. 正しいユーザー名とパスワードを入力した場合のログイン・ページ
この記事で最後に紹介する例は、XForms ベースの Ajax 記事です。
私は XForms の専門家ではありませんが、XForms は優れた標準だと思います。この記事では、クライアント・サイドの XForms アプリケーションを Protoype.js と Ajax の手法に融合させてみます。
リスト 13 に、この単純な XForms サンプルのコードを記載します。
リスト 13. index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xforms="http://www.w3.org/2002/xforms">
<head>
<title>XForms AJAX Example</title>
<xforms:model id="modelData">
<xforms:instance xmlns="">
<Data>
<First>First</First>
<Last>Last</Last>
<Email>email@email.com</Email>
</Data>
</xforms:instance>
</xforms:model>
<script src="prototype.js"></script>
</head>
<body>
<xforms:input ref="/Data/First">
<xforms:label>First: </xforms:label>
</xforms:input><br/>
<xforms:input ref="/Data/Last">
<xforms:label>Last: </xforms:label>
</xforms:input><br/>
<xforms:input ref="/Data/Email">
<xforms:label>Email: </xforms:label>
</xforms:input><br/><br/>
<button onclick="submit()">Submit</button>
<script>
function submit()
{
var m = $('modelData');
var base = m.getElementsByTagName('Data')[0];
var s = new XMLSerializer();
var data = ( s.serializeToString( base ) ).toString();
new Ajax.Updater( 'result', 'params.php',
{ method: 'post', parameters: 'data='+escape( data ) } );
}
</script>
<br/><br/>
<div id="result">
</div>
</body></html>
|
ファイルの先頭にあるのは、XForm の XML モデルです。その後に、フォーム自体を構成する一連の XForms ラベルおよび入力が続きます。Submit ボタンが呼び出す submit() JavaScript 関数は、XMLSerializer JavaScript オブジェクトを使って XForms データ・モデルを XML ストリングに変換し、この変換された XML ストリングが params.php (リスト 14 を参照) に送信されます。この params.php スクリプトは XML を HTML として返すため、ページの結果を見ることができるというわけです。
リスト 14. params.php
<?php
echo( htmlentities( $_POST['data'] ) );
?>
|
XForms アドオンがインストールされたブラウザーでこのページにアクセスすると、ページは図 11 のように表示されます。
図 11. Ajax 対応 XForms ページ
フォームに入力して Submit をクリックすると、図 12 のような結果になります。
図 12. Submit をクリックした後のページ
ここに表示されているのは、XML テキストに変換されてから params.php に送信され、それから result の <div> タグに返されたデータ・モデルです。
Ajax は HTML フォームを有効にする方法として大いに活用できます。この記事ではその一部をかじったに過ぎませんが、ページのコードに比較的簡単な変更を加えるだけでアプリケーションに対して何ができるのか、その実例を通して、大体の概念は理解していただけたことと思います。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Source code for forms application | x-ajaxxml9-forms.zip | 101KB | HTTP |
学ぶために
-
PHP ホーム・ページ: PHP プログラマーのための貴重な情報源にアクセスしてください。
-
Prototype ライブラリー: 動的 Web アプリケーションの開発を簡単にするために設計されたこの JavaScript フレームワークについて調べてください。
-
Scriptaculous JavaScript ライブラリー: この Prototype ベースのフレームワークで、Web サイトを成功させるための表示支援機能と効果を見つけてください。
-
Firefox 用 XForms アドオン: この記事で紹介した XForms サンプルを役立ててください。
-
Prototype.js 資料ページ: Prototype JavaScript ライブラリーの詳細を調べてください。ここには、Prototype の公式ブログをはじめとする豊富な資料へのリンクが記載されています。
-
jQuery: Prototype.js と同様の機能を提供するもう 1 つの JavaScript ライブラリーです。
-
Yahoo! UI Library: Yahoo! の Ajax 対応ツールキットです。
-
developerWorks XML ゾーン: developerWorks XML ゾーンで XML のすべてについて学んでください。
-
IBM XML 認証: XML や関連技術の IBM 認定開発者になる方法について調べてください。
-
XML technical library: 広範な技術に関する記事とヒント、チュートリアル、標準、そして IBM Redbok については、developerWorks XML ゾーンを参照してください。
-
developerWorks technical events and webcasts: これらのセッションで最新情報を入手してください。
議論するために
-
ディスカッション・フォーラムに参加してください。
-
XML ゾーンのディスカッション・フォーラム: XML を中心としたフォーラムに参加してください。
-
developerWorks XML zone: Share your thoughts: この記事についてのご意見、ご感想をお寄せください。このフォーラムを管理する XML ゾーンの編集チームは、読者からの投稿をお待ちしています。
Jack D. Herrington は、20 年以上の経験を持つシニア・ソフトウェア・エンジニアです。彼の著書には、『、『Code Generation in Action』、『Podcasting Hacks』、『PHP Hacks』の 3 冊があります。また、彼はこれまで 30 本以上の記事を書いています。連絡先は jherr@pobox.com です。