プロのように Ajax アプリケーションを開発する: 第 1 回 Prototype JavaScript ライブラリーと script.aculo.us を使う

もし皆さんが Web アプリケーションの開発を最近行っているとすると、おそらく皆さんは Ajax の開発を行っていることでしょう。Ajax は、もはや特別な場合にアプリケーションに追加するための風変わりなものではなく、Web 開発になくてはならないものになっています。これまでは、ある意味で、Ajax を使ってアプリケーションを機能強化する作業は複雑でした。クロスブラウザーの制約を処理し、大量の複雑な JavaScript を作成し、しかもそうした JavaScript の中にある魔法のような数字コードを学ぶ必要があるといったことは、Ajax 開発者が直面する困難のごく一部にすぎませんでした。幸いなことに、現在はそうした作業を容易に行えるようにしてくれる、オープンソースの JavaScript ライブラリーをいくつか入手することができます。この記事は 3 回シリーズの第 1 回として、Prototype JavaScript ライブラリーと script.aculo.us を使って曲を管理する Ajax アプリケーションを作成します。

Michael Galpin (mike.sr@gmail.com), Software architect, eBay

Michael Galpin は 1990年代後半から Web アプリケーションの開発に従事してきました。彼は、California Institute of Technology で数学の学位を取得しており、現在は、カリフォルニア州サンノゼにある eBay にアーキテクトとして勤務しています。



2008年 5月 13日

この 3 回シリーズの記事では、2 つの別々のオープンソース・プロジェクトである Prototype JavaScript ライブラリーと script.aculo.us の使い方を学びながら、Web 2.0 の Web サイト用の見事な Ajax アプリケーションを作成します。このシリーズの第 1 回である今回は、Prototype JavaScript ライブラリー (「参考文献」にリンクがあります) を取り上げます。この記事では、(この記事の執筆時点で) Prototype の最新リリースである、バージョン 1.6.0.2 を使います。Ajax では動的なデータを扱うため、サーバー・サイドの技術が必要です。この記事では PHP 5.2.1 を Apache 2.0.59 と MySQL 5.0.41 (「参考文献」を参照) と組み合わせて使用します。もちろん、皆さんは独自のプログラミング言語と Web サーバー、そしてデータベースを使用することができます。

developerWorks の Ajax リソース・センター

Ajax resource center にアクセスしてください。ここは Ajax アプリケーションを開発するための無料のツールやコード、そして情報が用意されたワンストップ・ショップです。Ajax のエキスパートである Jack Herrington がホストする活発な Ajax コミュニティー・フォーラムは、あなたが今まさに探している答え持っているかもしれない開発者仲間と交流する手段となります。

Prototype の紹介

検索を行ってみると、世の中には大量の JavaScript ライブラリーがあることがわかります。この理由は主に 2 つあります。第 1 に、JavaScript はブラウザーの言語であり、従ってソフトウェア開発の重要な一部となっているためです。誰もが JavaScript コードを作成しており、そのため大量の JavaScript ライブラリーが存在します。第 2 に、JavaScript は非常に扱いにくいものです。さまざまなブラウザーの間には思いがけない動作の違いがあるため、JavaScript 開発には多少の手間がかかることがよくあります。幸い、JavaScript ライブラリーによる抽象化を行うことでそうした手間を減らせることも多く、Prototype はそうしたライブラリーの 1 つです。

Prototype は非常に広範なライブラリーであり、たくさんの機能を持っています。Prototype の機能を利用すると、特に Ajax を扱う際に、一般的な作業を容易に行うことができます。Prototype には、Java™ や C++ スタイルの継承を JavaScript で実装するための非常に便利な方法や、HTML の DOM 要素のための拡張機能、また JSON を扱うためのユーティリティーなどが用意されています。この記事では、Prototype が Ajax に対してできることに焦点を絞り、その説明の中で Prototype の持つ他の機能をいくつか学ぶことにします。


Prototype の Ajax ライブラリーを使う

Prototype には Ajax 開発を支援するために設計された無数の機能があります。Prototype の人気が高い理由の 1 つは、Ajax のプログラミング方法に関する制限が何もないことです。例えば、(Ajax の基礎となっている機構の) XMLHttpRequest に応答するための一般的なパターンは 2 つあります。1 つのパターンでは、画面の一部を再描画するために使われる HTML で応答します。もう 1 つのパターンでは、データで応答し、そのデータの解析と再描画を他の JavaScript コードに任せます。Prototype はこの両方のパターンをサポートしています。では Prototype を使って、最初の手法、つまり HTML で応答する方法を調べてみましょう。

HTML を処理する

Prototype を利用すると、Ajax レスポンスに関してサーバーで生成された HTML を非常に容易に処理することができます。Prototype にはこのための専用の API が用意されており、サーバー・サイドのコードによって、Prototype専用の特別なフォーマットを持たない HTML フラグメントを簡単に生成することができます。これについて学ぶための最も容易な方法は、単に実際の動作を見てみることです。

この例では、曲を管理するためのアプリケーションを作成しています。このアプリケーションにはデータベースが必要です。リスト 1 はデータベースを作成するためのスクリプトを示しています。

リスト 1. データベースを作成するスクリプト
CREATE TABLE 'songs' (
  'id' int(11) NOT NULL auto_increment,
  'title' varchar(120) NOT NULL,
  'artist' varchar(120) NOT NULL,
  'genre' varchar(80) default NULL,
  'album' varchar(120) default NULL,
  'year' year(4) default '2008',
  PRIMARY KEY  ('id')
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1

先ほど触れたように、ここでは MySQL を使っていますが、このスクリプトを別のデータベース用に変更することも容易にできます。サンプル・データを作成するためには、この記事で提供されているスクリプト (「ダウンロード」セクションを参照) を使うことができます。

では、音楽のライブラリーを表示するためのページを作成しましょう。これは純粋な HTML ページです (リスト 2)。

リスト 2. index.html ページ
<html>
     <head>
          <title>MyTunes Library</title>
          <script type="text/javascript" src="prototype.js"></script>
          <script type="text/javascript">
               function loadTunes(){
                    new Ajax.Updater('tunesBox', 'list.php', { method: 'get' });
               }
          </script>
          <link  rel="stylesheet" href="tunes.css" type="text/css"/>
     </head>
     <body onload="loadTunes();">
          <div id="pageTitle">MyTunes</div>
          <div id="tunesBox">
               <img id="spinner" src="wait_spinner.gif" height="33" width="33"/>
          </div>
     </body>
</html>

ここから話が面白くなります。リスト 2 に示すページは、静的な、純然たる HTML と JavaScript コードから成るページです。これはつまり、このページをブラウザーでキャッシュしてサーバーの負荷を軽減できるということです。ページがロードされると、loadTunes() という JavaScript 関数が呼び出されます。ここで Prototype を使い、特に Prototype の Ajax.Updater オブジェクトを使います。Prototype は Ajax リクエストを送信するための、ブラウザーにとって適切なトランスポート機構を作成する作業を行います。この作業はかつて非常に手間のかかる作業だったのですが、Prototype のようなライブラリーが一般的になったおかげで、今やほとんど手間のかからない当たり前の作業となっています。

Ajax.Updater オブジェクト (リスト 2) は、ページ上の要素の ID と Web サーバーの URL、そしてオプションのマップを取得します。この場合はこのオブジェクトに tunesBox という ID を与えています。これを見ると、この ID を持つ div がページ上にあることがわかります。そして Ajax.Updater オブジェクトに「list.php」という URL を渡しています。Prototype はこの URL を呼び出し、そのレスポンスを取得し、それを tunesBox という div の中に入れます。最後に、ここで指定した唯一のオプションは、(デフォルトの) POST を使う代わりに HTTP の GET を使う、ということです。POST でもよいのですが、ここではデータを読み取るだけなので、REST の標準に従って GET を使いました。それでは、こうした指示によって Prototype が呼び出す PHP ファイルを見てみましょう。

リスト 3. list.php ファイル
<?php
  $message = "";
  $sql = "select * from songs";
  try{
     require_once(dirname(__FILE__)."/db.php");
     $result = mysql_query($sql,$conn);
     $list = array();
       if (!$result) {
         $message = "Could not successfully run query ($sql) from DB: " . mysql_error();
       } else if (mysql_num_rows($result) == 0) {
         $message = "Your library is empty!";
       } else {
         while ($row = mysql_fetch_assoc($result)){
               array_push($list, $row);
         }
       }
       mysql_free_result($result);
   } catch (Exception  $e) {
       $message = "Sorry there was an error: " . mysql_error();
   }
?>
<?php if ($message): ?>
     <div><span class="error"><?= $message ?></span></div>
<?php else: ?>
<table border="1" width="100%" cellpadding="8">
     <thead>
          <tr>
               <td>Name</td>
               <td>Artist</td>
               <td>Album</td>
               <td>Genre</td>
               <td>Year</td>
          </tr>
     </thead>
     <tbody>
     <?php foreach($list as $song): ?>
          <tr>
               <td><a href="edit.html?id=<?= $song["id"] ?>"><?= 
			                                 $song["title"] ?></a></td>
               <td><?= $song["artist"] ?></td>
               <td><?= $song["album"] ?></td>
               <td><?= $song["genre"] ?></td>
               <td><?= $song["year"] ?></td>
          </tr>
     <?php endforeach; ?>
     </tbody>
</table>
<?php endif; ?>

これは非常に単純な PHP スクリプトです。このスクリプトは先ほど作成されたテーブルに対してクエリーを実行し、その結果セットに対して繰り返し処理を行うことで HTML のテーブルを作成します。ここでは何も特別なことは行われていません。(ダウンロードしたコードの中にある JavaScript や CSS、他の PHP ファイルを含めて) PHP サーバーにすべてをデプロイし、それをブラウザーに表示します (図 1)。

図 1. MyTunes
MyTunes

このページはロードされると、Ajax と Prototype をシームレスに使ってサーバーから曲のテーブルを取得するはずです。ここで行われていることを詳細に調べるためには、Firefox の拡張機能として人気のある Firebug (「参考文献」にリンクがあります) のようなツールを使うことができます。図 2 は Firebug の出力を示しています。

図 2. MyTunes に対する Firebug のコンソール
MyTunes に対する Firebug のコンソール

このコンソールの Net (接続) > XHR タブを選択すると、Prototype が XMLHttpRequest を作成してくれたことがわかります。また Prototype はレスポンスに対する JavaScript のハンドラー関数も作成しています。この関数が実際に PHP スクリプトからのレスポンスを使って HTML の DOM を動的に変更しています。リスト 2 を見ると、たった 1 行のコードでこれらのすべてが実現されていることがわかります。これ以上簡単にならないほど Ajax が簡単になっているのです。先ほど触れたように、これは Ajax での一般的なパターンの 1 つです。もう 1 つのパターンでは、マークアップ (HTML) を使わずにデータのみをサーバーから返します。後者の方法にも Prototype が役立つ様子を調べてみましょう。

データを処理する

Ajax リクエストに対してサーバーが HTML を返すようにすると非常に便利ですが、そこには欠点もあります。サーバーからマークアップとデータの両方が返されるため、帯域幅が少し浪費されます。また、HTML に JavaScript (あるいは、少なくともイベント・リスナー) を追加する必要がある場合には非常に複雑になり、サーバーでクライアントの状態を追跡しなければならなくなります。場合によると、純粋にデータのみをサーバーから返した方が簡単になります。この場合にも、Prototype を使うと作業が簡単になります。ここでも例を使って説明しましょう。

先ほどの例で、各曲の名前が実際には別のページ (edit.html) へのリンクであることに気付いた人がいるかもしれません (リスト 4)。

リスト 4. edit.html ページ
<html>
     <head>
          <title>Edit a Song</title>
          <script type="text/javascript" src="prototype.js"></script>
          <script type="text/javascript" src="editor.js"></script>
          <link rel="stylesheet" href="tunes.css"/>
     </head>
     <body onload="loadSong();">
          <div id="pageTitle">Edit Song</div>
          <div id="tunesBox">
               <span id="spinner">
                    <img src="wait_spinner.gif" height="33" width="33"/>
               </span>
               <form id="songForm" onsubmit="catchSubmit();">
                    <input type="hidden" name="id" id="id"/>
                    <div id="name">
                         <span id="nameLbl">Name:</span>
                         <span id="title" onclick="edit(this)"?></span>
                    </div>
                    <div id="artistDiv">
                         <span id="artistLbl">Artist:</span>
                         <span id="artist" onclick="edit(this)"?></span>
                    </div>
                    <div id="albumDiv">
                         <span id="albumLbl">Album:</span>
                         <span id="album" onclick="edit(this)"?></span>
                    </div>          
                    <div id="genreDiv">
                         <span id="genreLbl">Genre:</span>
                         <span id="genre" onclick="edit(this)"?></span>
                    </div>
                    <div id="yearDiv">
                         <span id="yearLbl">Year:</span>
                         <span id="year" onclick="edit(this)"?></span>
                    </div>                                                  
               </form>
          </div>
          <div class="backLink">
               <a href="index.html">Back to MyTunes Library</a>
          </div>
     </body>
</html>

これも、すべて HTML の静的なページです。この場合には、ページがロードされると、このページは loadSong() という JavaScript 関数を呼び出します。この関数は別のファイル (editor.js) の中にあります。では、このファイルの loadSong 関数を見てみましょう (リスト 5)。

リスト 5. loadSong() JavaScript 関数
function loadSong(){
     var params = window.location.search.parseQuery();
function loadSong(){
     var params = parseQueryString();
     var id = params["id"];
     
     // create handler that will be invoked
     // when response is received from server
     var handler = function(xhr){
          // use responseJSON property added by Prototype
          var json = xhr.responseJSON;
          // check for error
          if (json.error){
               // display the error
          }
          var song = json.song;
          // clear the spinner
          // use Prototype's $() shortcut notation
          $("spinner").innerHTML = "";
          // set the display data
          $("id").value = song.id;
          $("title").innerHTML = song.title;
          $("artist").innerHTML = song.artist;
          $("album").innerHTML = song.album;
          $("genre").innerHTML = song.genre;
          $("year").innerHTML = song.year;     
     };
     
     // create options for 
     var options = {
          method : "get",
          onSuccess : handler,
          parameters : { "id" : id }
     };
     
     // send the request
     new Ajax.Request("song.php", options);
}

window.location.search という式によって、そのページの URL のクエリー・ストリングにアクセスすることができます。この場合のクエリー・ストリングは「?id=2」のようなものになります。次に parseQuery() 関数のうちの 1 つが使われています。この関数は window.location.search オブジェクトに固有の特別な関数ではありません (window.location.search オブジェクトは単なるストリングです)。parseQueryString() 関数は Prototype が JavaScript のストリング・クラスに追加する関数なので、どのストリングでもこの関数を呼び出すことができます。parseQueryString() 関数はその名前どおりのことをします。つまり、当該ページの URL のクエリー・ストリングを名前と値のペアのマップに変換します。これによって、その前のページから渡された ID パラメーターを取得することができます。次に、このパラメーターを使って PHP スクリプト song.php を呼び出します。song.php は曲をロードして返す、サーバー・サイドのスクリプトです。このスクリプトのコードをリスト 6 に示します。

リスト 6. song.php のスクリプト
<?php
     header('Content-Type: application/json');
     $message = "";
     $resp = array();
     $sql = "select * from songs where id =" . $_REQUEST["id"];
     try{
          require_once(dirname(__FILE__)."/db.php");
          $result = mysql_query($sql,$conn);
          $song = array();
          if (!$result) {
              $message = "Could not successfully run query ($sql) from DB: " . 
			                                            mysql_error();
          } else if (mysql_num_rows($result) == 0) {
              $message = "No song found with that id!";
          } else {
               $song = mysql_fetch_assoc($result);
          }
          $resp["song"] = $song;
          mysql_free_result($result);
     } catch (Exception  $e) {
          $message = "Sorry there was an error: " . mysql_error();
     }
     $resp["error"] = $message;
     echo(json_encode($resp));
?>

リスト 6 はデータベースに対してクエリーを実行します。ここで JSON コードを返送していることに注意してください。データの転送に XML を使うこともできたのですが、クライアントでは JSON の方が少し効率的であり、また作業も容易です。適切に JSON を返送するためには、コンテンツ・タイプを application/json と設定します。こうすることはブラウザーに対して適切なだけではなく、Prototype でも要求されていることです。Prototype で必ずこうするように厳密に要求されているわけではありませんが、こうすることによって Prototype はレスポンスを JSON として識別できるため、開発者の作業が少し楽になるのです。最後に、データを JSON として返すために、PHP に組み込みの json_encode 関数を使います。(どのような言語を使用する場合にも、これと似たライブラリー関数があるはずです。)

今度はリスト 5 に戻り、作成されたレスポンス・ハンドラー (var handler = function ...) を見てみましょう。Prototype は、返される JSON が安全かどうかを自動的に評価します。つまり Prototype は、このレスポンス・ハンドラーに渡すオブジェクトの responseJSON プロパティーを使って安全性の評価を行います。これによって song.title などのデータに容易にアクセスできるようになります。今度は $("spinner") で始まる行を見てみましょう。$() 記法は Prototype に用意されているショートカットであり、要はおなじみの document.getElementById() 関数へのショートカットです。つまり $("spinner") は基本的に document.getElementById("spinner") と等価です。後で説明するように、Prototype は実際には (戻りオブジェクトの型に応じて) 少し余分なメソッドを戻りオブジェクトに追加します。Prototype を利用すると、$("title").innerHTML = song.title のような単純な構文を使って表示を初期化することができます。これをブラウザーで表示すると 図 3 のようになるはずです。

図 3. 初期化後の Edit ページ
初期化後の Edit ページ

この場合も、Firebug を使うとサーバーからフェッチされるデータをウォッチすることができます (図 4)。

図 4. ロードされる曲のデータの Firebug による表示
ロードされる曲のデータの Firebug による表示

さて、タイトルは「Edit Song (曲を編集)」となっていますが、このページは読み取り専用のように見えます。しかし属性の値の 1 つをクリックすると、その値を編集することができます (図 5)。

図 5. 曲の編集
曲の編集

タブ・キーで他の項目に移るか、あるいは Enter キーを押すと、このデータはサーバーに送信され、データベースの中で更新されます。データをサーバーに送信するためのコードも editor.js ファイルの中にあり、これをリスト 7 に示します。

リスト 7. 曲を更新するための JavaScript
function makeText(input){
     // save record
     var formData = $("songForm").serialize(true);
     saveData(formData);
     // go back to display
     input.parentNode.innerHTML = input.value;     
}

function saveData(song){
     var handler = function(xhr){
          var json = xhr.responseJSON;
          if (json.error){
               // display the error
          }
     };
     var options = {
          method : "post",
          onSuccess : handler,
          parameters : song
     };
     new Ajax.Request("update.php", options);                    
}

このコードは makeText() 関数から始まっています。今度は Prototype のショートカット、$("songForm") を使用すると、Prototype によって機能強化された Form オブジェクトが取得されます。これによって、新しい関数である serialize 関数が得られ、この関数を呼び出せるようになります (serialize 関数はフォームの中のすべての入力フィールドを取得し、それらの名前と値のハッシュを作成します)。そのハッシュを saveData() 関数に渡します。そして再度 Prototype を使って Ajax リクエストをサーバーに送信します。この場合にはデータが変更されており、また update.php が呼び出されるため、POST が使われていることに注目してください (リスト 8)。

リスト 8. update.php スクリプト
<?php
     header('Content-Type: application/json');
     $message = "";
     $clause = "";
     foreach ($_POST as $key => $value){
          if ($key != "id"){
               $clause = $key . " = '" . $value . "' ";
          }
     }
     $sql = "update songs set " . $clause . " where id=" . $_POST["id"];
     try{
          require_once(dirname(__FILE__)."/db.php");
          $result = mysql_query($sql,$conn);
          if (!$result) {
              $message = "Could not successfully run query ($sql) from DB: " . 
			                                         mysql_error();
          }
          mysql_free_result($result);
     } catch (Exception  $e) {
          $message = "Sorry there was an error: " . mysql_error();
     }
     $resp["error"] = $message;
     echo(json_encode($resp));
?>

この例でも、非常に簡単に PHP が処理されていることがわかります。この Ajax リクエストが、変更される曲の ID とフィールドのみを返送していることに注目してください。これは Ajax がデータベースの変更に関して効率的なことを示す一例です。こうした動作もすべて Firebug の中に表示されています (図 6)。

図 6. 曲の更新を表示している Firebug
曲の更新を表示している Firebug

Prototype には、Ajax リクエストを送信するためのメソッドだけではなく、送信データにアクセスするメソッドやレスポンス・データにアクセスするメソッド、また表示を変更するためのメソッドなど、便利なメソッドが数多く用意されています。


まとめ

Ajax の世界は、ほんの数年前とは異なり、まったく未知の領域ではなくなっています。賢明な開発者は、無料で入手できるオープンソースのライブラリーを最大限に活用することによって、一部の Ajax プログラミングで偶然に起こりうる複雑さを軽減することができます。Prototype は、そうした場合の Ajax プログラミングの単純化を最大の目的とする強力なライブラリーです。Prototype は Ajax 開発のあらゆる側面を単純化できる上、コンテンツ (サーバーから返送される HTML) 中心の Ajax にもデータ中心の Ajax にも力を発揮します。経験豊富な Web 開発者にとって、Prototype はツールボックスの中にぜひとも用意しておく必要のあるツールと言えます。


ダウンロード

内容ファイル名サイズ
Part 1 sample codewa-aj-ajaxpro1.zip39KB

参考文献

学ぶために

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

  • Prototype は Ajax プログラミングを単純化するための強力な関数を持つ JavaScript ライブラリーです。
  • Firefox 用の Firebug 拡張機能を入手してください。
  • PHP を入手してください。
  • Apache を入手してください。
  • MySQL を入手してください。

コメント

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=Web development, Open source
ArticleID=312939
ArticleTitle=プロのように Ajax アプリケーションを開発する: 第 1 回 Prototype JavaScript ライブラリーと script.aculo.us を使う
publish-date=05132008