ASP.NET にスパイスを加える: AJAX
Karl Seguin
September 2005
日本語版最終更新日 2006 年 1 月 13 日
適用対象: AJAX (Asynchronous JavaScript And XML) Microsoft AJAX.NET Microsoft ASP.NET
要約: AJAX (Asynchronous JavaScript And XML) を使用して、動的で応答性の高い Microsoft ASP.NET アプリケーションを作成する方法を説明します。サンプル プログラム ファイル内では実際のコメント行は英語で書かれていますが、この記事内では説明目的で日本語で書かれています。この記事には英語のページへのリンクも含まれています。
この記事のコード サンプルのダウンロード AjaxASPNETCS.msi (C#)
この記事のコード サンプルのダウンロード AjaxASPNETVB.msi (Visual Basic)
目次
はじめに AJAX について AJAX for ASP.NET AJAX を実践する AJAX を効果的に利用する まとめ
はじめに
Web プログラミングの開始以来、Web アプリケーションとデスクトップ アプリケーションとの間に多くのトレードオフが存在してきました。たとえば、Web アプリケーションには、デスクトップ アプリケーションと同じタイプの高度なユーザー インターフェイスが備わっていないことは一般に認められています。反対に、Web アプリケーションはプラットフォームに依存せず、開発しやすい機構を備えています。Web 開発者にとって常に葛藤の場であった 1 つの領域は、応答性の高いアプリケーションの提供という一見簡単な課題でした。
従来は、Web サーバーに新しい要求を送信しなければ、ユーザー入力への応答を取得できませんでした。一部の例では、開発者が JavaScript を使用してすべての応答をクライアント上に読み込み、ユーザー エクスペリエンスを向上できる場合もありました。この手法は、たとえば、選択した国に基づいて都道府県の一覧を動的に読み込む場合によく使われています。残念ながら、ほとんどの場合、ポストバック処理も JavaScript にすべてを読み込む処理も正解であるとは実感できませんでした。ポストバック処理では UI の切断が多く、すべてを読み込む処理ではクライアント上に管理し難いほどの多量のデータが必要になります (多くの場合は読み取りにくい JavaScript になります)。AJAX は、新しい中間的な代替案であり、応答が迅速で滑らかな操作を持続するサーバー ベースのアプリケーションを実現します。
AJAX について
AJAX は、Asynchronous JavaScript And XML の省略形であり、1 つのテクノロジではなく、複数のテクノロジのグループ化です。AJAX では、通信テクノロジ (通常は SOAP および XML) を使用して非同期にサーバーと要求/応答を送受信し、プレゼンテーション テクノロジ (JavaScript、DOM、HTML、および CSS) を利用して応答を処理します。今日では、ほとんどのブラウザが必要なテクノロジをサポートしているため、AJAX を使用したアプリケーションは適正であると見なされています。AJAX の詳細については、Wikipedia の 「AJAX」を参照してください。
AJAX とは実際に何なのでしょうか。AJAX を使用すると、ブラウザを更新する必要なしに、JavaScript 呼び出しを通じてサーバー側のメソッドを実行できます。ユーザーからは見えないバックグラウンドで発生する小規模の要求/応答だと考えてください。AJAX が何であるか、まだよくわからない場合は、よく知られている 2 つの Google の例、Google サジェストと Google ローカルをご覧ください。AJAX を初めて体験する場合は、この 2 つのアプリケーションの迅速な応答に興奮してゾクゾクするでしょう。
AJAX for ASP.NET
AJAX を機能させるために、数多くの技術が投入されています。この記事を読んでいる皆さんは、たぶん、AJAX の内部機構の理解に何時間も何日も費そうとしているのではなく、今日にでも AJAX のアプリケーションの作成を開始し、現在抱えている問題を解決したいと思っていることでしょう (内部的な動作の説明を求めているのであれば、私はその適任ではありません)。すぐに開始するために開発者が使用できるツールが多数あります。具体的に言うと、この記事では Michael Schwarz が記述した無償で入手できるオープン ソースの Ajax.NET に注目しています。Ajax.NET を使用すれば、実装の詳細を考慮せずに .NET を利用でき、拡張も可能です。Microsoft ASP.NET 2.0 では、クライアント コールバック機能 (英語) を通じて独自の非同期コールバックを導入しており、また、最近の発表 (英語) では、AJAX の実装 (コードネーム "Atlas") が完成間近であると報じられています。
混乱させるような用語ですが、この記事で AJAX について言及している場合は、クライアントからサーバー側の関数を非同期に呼び出すためのフレームワーク全体のことを表します。また、Ajax.NET とは、AJAX フレームワークを利用したソリューションの構築に役立つ特定の実装のことです。
ASP.NET 2.0 のクライアント コールバック機能の詳細については、「Bertrand Le Roy's blog」 (英語) を参照してください。
AJAX を実践する
この記事の残りの部分の大半は、Ajax.NET を使用して AJAX の機能を活用している有意義な例を 3 つ紹介します。この記事には、Microsoft C# と Microsoft Visual Basic .NET のコードが含まれます。両方のコードが提供されている場合と、どちらか一方のみの場合があります。このすべてを生み出すコードは極めて簡単であり、C# または Visual Basic .NET の開発者は、どちらのコードでも簡単に理解できます。この記事では、ダウンロードして入手できる C# および Visual Basic .NET のサンプル プロジェクトを有効な実行コードとして提供しています。例を開始する前に、Ajax.NET の設定および使用方法についての教材が必要です。
Ajax.NET
AJAX.NET のドキュメント (英語) および Web サイト (英語) は、作業を開始して実行するために必要な情報を開発者に提供します。このテクノロジの具体的な使用例を紹介する前に、知っておく必要がある主な手順について簡単に説明します。
まず、AJAX.NET プロジェクトの Web サイト (英語) から AJAX ファイルをダウンロードして解凍し、ASP.NET プロジェクトを (Visual Basic .NET または C# のいずれかで) 作成し、AJAX.dll ファイルへの参照を追加することから始めます。その他の構成手順としては、次のコードを web.config ファイルの <system.web> 要素の中に追加するだけです。 <configuration>
<system.web>
<httpHandlers>
<!-- Register the ajax handler -->
<add verb="POST,GET" path="ajax/*.ashx"
type="Ajax.PageHandlerFactory, Ajax" />
</httpHandlers>
...
...
</system.web>
</configuration>
JavaScript を通じてサーバー側の関数を使用できるようにするには、次の 2 点を完了する必要があります。第一に、問題の関数に Ajax.AjaxMethodAttribute を付加する必要があります。2 番目に、Page Load イベントの中で、Ajax.Utility.RegisterTypeForAjax の呼び出しを通じてこれらの関数を含むクラスを登録する必要があります。複雑そうに聞こえても心配しないでください。実際にはコードに 2 行を追加するだけです。それでは例を見てください。 //C# public class Sample : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
//対象となるサーバー側関数を
//含むクラスを登録します
Ajax.Utility.RegisterTypeForAjax(typeof(Sample));
}
[Ajax.AjaxMethod()]
public string GetMessageOfTheDay()
{
return "Experience is the mother of wisdom";
}
}
'VB.NET
Public Class Sample
Inherits System.Web.UI.Page
Private Sub Page_Load(sender AsObject, e As EventArgs)
Handles MyBase.Load
'対象となるサーバー側関数を
'含むクラスを登録します
Ajax.Utility.RegisterTypeForAjax(GetType(Sample))
End Sub
<Ajax.AjaxMethod()> _
Public Function GetMessageOfTheDay() As String
Return "Experience is the mother of wisdom"
End Function
End Class
上記の例では、まず最初に、Ajax に対応したメソッドがあるかどうか Sample クラスを調べるように指定しています。この例では、実際のページとクラスが同じですが、任意の .NET クラスを使用でき、また複数のクラスを登録できます。Ajax.NET は、特定のクラスの中に、AjaxMethodAttribute が付加されたメソッドがあるかどうかを調べます。Sample クラスには、GetMessageOfTheDay という 1 つのメソッドがあります。
完了したら、あとは JavaScript で利用するだけです。Ajax.NET によって、AjaxMethod と同名の関数 (この例では GetMessageOfTheDay) を公開している登録済みのクラス (この例では Sample) と同名の JavaScript 変数が自動的に作成されます。次に例を示します。 <script language="javascript">
Sample.GetMessageOfTheDay(GetMessageOfTheDay_CallBack);
function GetMessageOfTheDay_CallBack(response)
{
alert(response.value);
}
</script>
JavaScript の GetMessageOfTheDay のパラメータは、サーバー側の対応するメソッドと同じパラメータ (この例ではなし) と、JavaScript コールバック関数であり、これらは実行および完了時に応答を渡すために使用されます。ここに、AJAX の非同期の特質が活用されているのを確認できます。GetMessageOfTheDay の呼び出しによって、他の JavaScript コードを実行できなくなったり、ユーザーがページでの作業を継続できなくなったりすることはありません。サーバー側の処理が完了すると、Ajax.NET は指定されたコールバック関数 GetMessageOfTheDay_CallBack を呼び出し、サーバー側の戻り値が含まれた応答を渡します。
サーバー側のコードと JavaScript コードのマッピングは、紛らわしくなる場合があります。図 1 に、サーバー側のコードと JavaScript コード、およびこの 2 つのマッピングを示します。

図 1. サーバー側コードと JavaScript コードのマッピング
他にも、Ajax.NET の興味深い点は多数あります。たとえば、.NET 型のサポートや豊富なコールバックの応答など、単に価値と呼ぶ以上のものがあります。次の例を見ると、一部の機能が明らかになり、成功するアプリケーションの作成に AJAX がどのように役立つかを想定できます。
サンプル 1: ドロップダウン リストをリンクする
この記事の始めに、DropDownList を別のページにリンクするための従来の 2 つのアプローチを簡単に説明しました。選択されたインデックスが変更されると、ページがポストバックされるか、すべての可能なデータが JavaScript の配列に読み込まれて動的に表示されます。ここでは、AJAX を使用して、この 2 つの解決策に取って替わる方法を示します。
最初に、データのインターフェイスを調べ、そこから例を引き出します。データ アクセス層は、2 つのメソッドを公開します。最初のメソッドはシステムがサポートする国の一覧を取得し、2 番目のメソッドは国 ID を受け取って都道府県の一覧を返します。これは純粋なデータ アクセスであるため、メソッドのシグネチャを調べるだけで済ませます。 //C#
public static DataTable GetShippingCountries();
public static DataView GetCountryStates(int countryId);
'VB.NET
Public Shared Function GetShippingCountries() As DataTable
Public Shared Function GetCountryStates(ByVal countryId As Integer)
As DataView
今度は、反対側の層に転じて簡単な Web フォームを作成します。 <asp:DropDownList ID="countries" Runat="server" />
<asp:DropDownList ID="states" Runat="server" />
<asp:Button ID="submit" Runat="server" Text="Submit" />
Page_Load イベントは、上記の Web フォームと同様に簡単であり、よく使われています。ここでは、データ アクセス層を使用して使用可能な国を取得し、それを countries DropDownList にバインドします。 //C#
if (!Page.IsPostBack)
{
countries.DataSource = DAL.GetShippingCountries();
countries.DataTextField = "Country";
countries.DataValueField = "Id";
countries.DataBind();
countries.Items.Insert(0, new ListItem("Please Select", "0"));
}
ここから、このコードは一般的ではなくなります。最初に、JavaScript から呼び出すサーバー側の関数を作成します。 'VB.NET
<Ajax.AjaxMethod()> _
Public Function GetStates (ByVal countryId As Integer) As DataView
Return DAL.GetCountryStates(countryId)
End Function
これは通常のすべての関数と同じです。取得する国の ID を受け取り、DAL に要求を渡します。唯一の違いは、AjaxMethodAttribute をメソッドに付加したことです。サーバー側の最後の作業として、上記のメソッド (この例では分離コード) を含むクラスを RegisterTypeForAjax の呼び出しを通じて Ajax.NET に登録します。 //C#
Ajax.Utility.RegisterTypeForAjax(typeof(Sample));
'VB.NET
Ajax.Utility.RegisterTypeForAjax(GetType(Sample))
もうすぐ完了です。あとは JavaScript から GetStates を呼び出し、応答を処理するだけです。この例では、ユーザーが国の一覧で新しい項目を選択したとき、GetStates を論理的に呼び出します。そうするには、JavaScript の onChange イベントにフックします。次のように、Web フォームのコードにいくらか変更を加えます。 <asp:DropDownList onChange="LoadStates(this)"
ID="countries" Runat="server" />
JavaScript の LoadStates 関数は、Ajax.NET によって作成されたプロキシを使って、非同期要求を発行する役割を担います。既定では、Ajax.NET によって作成されるプロキシは、<登録済み型名>.<サーバー側メソッド名> の形式をとります。この例では、Sample.GetStates となります。また、国 ID パラメータと、サーバー側関数の実行の完了時に Ajax.NET が呼び出すコールバック関数も渡す必要があります。 //JavaScript
function LoadStates(countries)
{
var countryId = countries.options[countries.selectedIndex].value;
Sample.GetStates(countryId, LoadStates_CallBack);
}
最後の手順は、LoadStates_CallBack 関数での応答の処理です。豊富な .NET 型のサポートは、Ajax.NET の最も生産的な機能であると言えます (既に何度も指摘しているとおりです)。サーバー側関数は、DataView を返します。JavaScript は DataView について何を知っているでしょうか。何も知りません。ただし、JavaScript はオブジェクト指向言語であり、Ajax.NET は .NET の DataView に類似したオブジェクトを作成するだけでなく、関数の戻り値を JavaScript のクローンにマップします。JavaScript の DataView は、実際の DataView のレプリカにすぎず、現在ではすべての行をループ処理して列の値にアクセスする機能 (RowFilter または Sort プロパティの設定などの機能) の他は、ほとんどサポートされていない点に注意してください。 function LoadStates_CallBack(response)
{
//サーバー側コードが例外を投げてきた場合
if (response.error != null)
{
//もっと良い方法があるはずです
alert(response.error);
return;
}
var states = response.value;
//応答が予想に反していた場合
if (states == null || typeof(states) != "object")
{
return;
}
//ドロップダウンリストの状態を獲得します
var statesList = document.getElementById("states");
statesList.options.length = 0; //ドロップダウンリストの状態をリセットします
//JavaScript では length であって、Length でないことに注意してください
for (var i = 0; i < states.length; ++i)
{
//行の列は名前付きプロパティのように表示されます
statesList.options[statesList.options.length] =
new Option(states[i].State, states[i].Id);
}
}
いくらかエラー チェックを行った後、前述の JavaScript は都道府県のドロップダウン リストを取得し、応答の値をループ処理し、ドロップダウン リストにオプションを動的に追加します。コードはよく整理されてわかりやすく、C# や Visual Basic .NET に不思議なくらい類似しています。私自身、サーバー側の変数に基づいて JavaScript の配列を作成しリンクした開発者として、これで実際に動作することがいまだに信じ難い状態です。
まだ解決されていない重要な問題が 1 つあります。DropDownList は JavaScript で動的に作成されているため、その項目は ViewState の一部ではなく、管理の対象となりません。したがって、ボタンの OnClick イベント ハンドラが追加の作業を行う必要があります。 'VB.NET
Private Sub submit_Click(sender As Object, e As EventArgs)
Dim selectedStateId As String = Request.Form(states.UniqueID)
'何らかのユーザー認証が必要です
states.DataSource =
DAL.GetCountryStates(Convert.ToInt32(countries.SelectedIndex))
states.DataTextField = "State"
states.DataValueField = "Id"
states.DataBind()
states.SelectedIndex =
states.Items.IndexOf(states.Items.FindByValue(selectedStateId))
End Sub
まず、states.SelectedValue プロパティを使用できないため、Request.Form を使用する必要があります。次に、ユーザーに一覧を再表示するには、同じデータ アクセス メソッドを再利用して、都道府県の DropDownList をバインドする必要があります。最後に、選択された値をプログラムで設定する必要があります。
サンプル 2: ドキュメントをロックする
次の例では、より完全な機能を取り上げ、それを AJAX で改善します。ここでは簡単なドキュメント管理システムの例を示します。すべての適正なドキュメント管理システムと同様に、同時実行に対する制御を備える必要があります。つまり、2 人のユーザーが同じドキュメントを編集しようとした場合の処理が必要です。この例では、ある種のロック機構を作成して、既に編集されているドキュメントを他のユーザーが編集できないようにします。ここでは AJAX を使用して、ロック機構に対するユーザー エクスペリエンスを向上します。最初に、既に編集中であったためにユーザーが試みても編集できなかったドキュメントのキューを作成し、使用可能になったときに自動的に通知を送るようにします。2 番目に、ユーザーがブラウザを閉じるか、どこか別のリンクにナビゲートした場合は、ドキュメントを確実にアンロックします。この最後の機能は、ドキュメントが永久にロックされたままにならないことを確実にするうえで便利です。この記事では、AJAX の実装に直接関連のない機能については説明しません。ただし、上記の場所からダウンロードしたプロジェクトには、それらの機能もすべて含まれています。
最初に、ユーザーがドキュメントの編集をしようとして、ドキュメントに対する排他的なロックの取得を試み、失敗した場合は、ドキュメントをユーザーのキューに追加し、ユーザーをメイン ページに戻します。これは AJAX に固有の機能ではありませんが、この例の必要なコンテキストを提供するためにコードにざっと目を通します。編集する Page の OnLoad イベントに、次のコードを追加します。 //C#
if (!Page.IsPostBack)
{
//ユーザー入力の確認が必要です
Document document = GetDocument(Request.QueryString["id"]);
//ドキュメントは取得したが編集ができません
if (!Locker.AcquireLock(document))
{
//そのユーザーの監視対象ドキュメントのリストに追加します
User.CurrentUser.AddDocumentToQueue(document.DocumentId);
Response.Redirect("DocumentList.aspx");
}
//これでドキュメントの編集が可能になりました
//...
}
重要な行は、ドキュメントを現在のユーザーのキューに追加して、セッションにドキュメントを追加する箇所です。次に、ユーザー コントロールを作成し、任意のページに配置して、キュー内のドキュメントが使用可能になったときにこれを使用してユーザーに通知します。ユーザー コントロールは、単一の AJAX メソッドと、クラスを AJAX に登録するために必要なコードで構成されます。 'VB.NET
Private Sub Page_Load(s As Object, e As EventArgs)
Handles MyBase.Load
Ajax.Utility.RegisterTypeForAjax(GetType(UnlockNotifier))
End Sub
'ドキュメントのキューをループして、ドキュメントが使用できるかを確認します
<Ajax.AjaxMethod()> _
Public Function GetUnlockedDocuments() As DocumentCollection
'そのユーザーのキューのドキュメントをすべて取得します
Dim queuedDocument As ArrayList = User.CurrentUser.DocumentQueue
Dim unlocked As DocumentCollection = New DocumentCollection
For Each documentId As Integer In queuedDocumentIds
'キュー内のドキュメントのロックが解除された場合の処理
If Not Locker.IsLocked(documentId) Then
unlocked.Add(Document.GetDocumentById(documentId))
End If
Next
Return unlockedDocuments
End Function
あとは、要求を作成し応答を処理するために JavaScript が必要です。ロックを解除されたドキュメントに関する情報がある場合は、応答に基づいて、動的に作成されるテーブルの中に保存します。そのためには、まず HTML の作業に取り掛かります。 <div id="notifyBox" style="display:none;">
<b>The following queued documents can now be edited</b>
<table cellpadding="5" cellspacing="0"
border="0" style="border:1px solid #EEE;"
id="notifyTable">
</table>
</div>
DIV タグを使用して、使用可能なドキュメントがない (またはユーザーのキューにドキュメントがない) 場合はすべてを非表示にし、TABLE タグを使用して結果を表示します。また、ポーリング システムを使用して、キュー内にドキュメントがあるかどうかを確認します。基本的には、遅延を使用してサーバー側のメソッドを呼び出し続け、結果を表示します。最初の呼び出しは、ページを読み込んだときに発生し、後続の呼び出しは X 秒ごとに発生するように時間指定されます。 <script language="javascript">
window.setTimeout("PollQueue();", 2000);
//キュー内のドキュメントが、多数のユーザーが使用している実際の
//システムにリリースされているかどうかを 2 秒ごとにチェックします
//2 秒であれば、システムにあまり大きな付加はかからないはずです
//キューにユーザーのドキュメントが実際にあるかどうかを最初にチェック
//してもいいが、何らかのパフォーマンス テストが必要になります
function PollQueue()
{
//UnlockNotifier は Ajax.NET で登録した型です
//GetUnlockedDocuments は、その型のうち、jaxMethod 属性を
//持つメソッドです
UnlockNotifier.GetUnlockedDocuments(PollQueue_CallBack);
//2 秒ごとに呼び出します
window.setTimeout("PollQueue();", 2000);
}
</script>
あとは応答を処理するだけです。これは前の例のコードに類似しています。最初に、エラーがあるかどうかをチェックして、応答を取得します。次に、使用可能なドキュメントをループ処理し、この例ではテーブルに行と列を追加して、HTML を動的に作成します。 function PollQueue_CallBack(response)
{
var notifyBox = document.getElementById("notifyBox");
var notifyTable = document.getElementById("notifyTable");
//テーブル通知ボックスが見つからない場合
if (notifyBox == null || notifyTable == null)
{
return;
}
//サーバー側コードが例外を投げてきた場合
if (response.error != null)
{
notifyBox.style.display = "none";
alert(response.error); //もっと良い方法があるはずです
return;
}
var documents = response.value;
//応答が予想に反していた場合
if (documents == null || typeof(documents) != "object")
{
notifyBox.style.display = "none";
return;
}
for (var i = 0; i < notifyTable.rows.length; ++i)
{
notifyTable.deleteRow(i);
}
for(var i = 0; i < documents.length; ++i)
{
var row = notifyTable.insertRow(0);
row.className = "Row" + i%2;
var cell = row.insertCell(0);
cell.innerHTML = documents[i].Title;
cell = row.insertCell(1);
var date = documents[i].Created;
cell.innerHTML = date.getDay() + "/" + date.getMonth()
+ "/" + date.getYear();
cell = row.insertCell(2);
cell.innerHTML = "<a href='DocumentEdit.aspx?id="
+ documents[i].DocumentId + "'>edit</a>";
}
notifyBox.style.display = "block";
}
最後に、簡単な改善を加えるため、ユーザーがブラウザを閉じるか、別のリンクに移動するか、[戻る] ボタンをクリックした場合に、編集中のドキュメントが自動的にアンロックされるようにします。通常では、JavaScript の OnBeforeUnLoad イベントまたは OnUnload イベントにフックして、ページの読み込み時にクリーンアップを実行して自動的に終了する新しい小型のポップアップを開くことによって、この処理を実行します。ユーザーが独自に使用しているポップアップは適切であったとしても、一部には正しく機能しない場合もあり、ポップアップがブロックしてドキュメントが永久にロックされたままになることがあります。この問題を解決するために、この例では 2 つの JavaScript イベントに引き続き依存しますが、ポップアップを起動せずに、AJAX を通じてサーバー側のメソッドを実行します。ドキュメントを編集するためのページ、つまりロックを設定するページに対して、簡単な JavaScript を追加します。 <script language="javascript">
//ユーザーがブラウザを終了した場合や [戻る] ボタンをクリックした
//場合にドキュメントのロックが必ず解除されるようにします
window.onbeforeunload = ReleaseLock;
function ReleaseLock() {
Locker.ReleaseDocument(<%=DocumentID%>);
}
</script>
ここで、DocumentId は、分離コードで定義および設定された変数です。代替策として、DocumentId をセッション内に保存し、サーバー側の ReleaseDocument でアクセスすることも可能です。基本的に、ReleaseDocument はロックされたドキュメントの一覧からドキュメントを削除します。
サンプル 3: フォーラムの件名を検索する
この記事で取り上げる最後の例では、既存のアプリケーションを変更します。この構想は、最初に Josh Ledgard (英語) 氏から提案され、MSDN フォーラム (英語) で実行されている機能であると聞いています。その目標は、重複ポストの件数を抑制するというだけでなく、質問のあるユーザーが、自らの手で問題を解決できるよう援助することにあります。基本的に、ユーザーがフォーラムで新しい質問をする場合、ユーザーは件名と質問を入力し、ほとんどの場合は同じ質問が既に提出および回答されているかどうかを検索して確認していません。「AJAX」と入力してみてください。ユーザーが件名を入力し終わったとき (Tab キーを押してフィールド外に移動したとき)、件名に基づいて非同期にフォーラムを検索し、押し付けがましくならないように、ユーザーに結果を提示します。結果が役に立つ場合もあれば、そうでない場合もあります。
これを実行するために、asp.NETPRO Reader's Choice Award で最高のフォーラム アプリケーションとして選択された CommunityServer に変更を加えます。ダウンロードできるサンプルには、このセクション (またはフォーラム) のコードは含まれていませんが、CommunityServer の詳細については http://communityserver.org/ (英語) を参照し、次のコード スニペットを適用できます。
CommunityServer をインストールし、Ajax.NET の設定が完了したら (web.config への参照とハンドラの追加)、わずかな変更を加えるだけで目的の機能が得られます。最初に、CommunityServerForums プロジェクトの CreateEditPost.cs ファイルから取り掛かります。これはユーザーがアクセスして新しいポストを追加するページの分離コードであると考えてください。ここに、AJAX に対応した関数を追加します。 //C#
[Ajax.AjaxMethod()]
public static ArrayList Search(string search)
{
SearchQuery query = new SearchQuery();
query.PageIndex = 0; //検索結果の最初の10個を取得します
query.PageSize = 10;
query.UserID = Users.GetUser().UserID;
query.SearchTerms = search;
return new ForumSearch().GetSearchResults(query).Posts;
}
CommunityServer に既に組み込まれている検索機能を利用して、関数をラップするだけで済みます。いつものように、型を Ajax.NET に登録する必要があります。これは同じファイルの InitializeSkin 関数で実行します (この関数は Page_Load だと考えてください)。 //C#
Ajax.Utility.RegisterTypeForAjax(typeof(CreateEditPost));
JavaScript の作業に入る前に、最後に 1 つサーバー側の変更が必要です。サーバーから返される ArrayList に含まれる ForumPost など、Ajax.NET に返されるカスタム クラスに、Serializable 属性を付加する必要があります。それには、CommunityServerForums プロジェクトの Components/ForumPost.cs ファイルの中に、属性を追加するだけです。 //C#
[Serializable]
public class ForumPost : Post
{
...
}
プレゼンテーション側では、CommunityServerWeb プロジェクトの Themes/default/Skins/View-EditCreatePost.cs を変更するだけで済みます。最初に、件名テキスト ボックスの onBlur イベントにフックします。 <asp:textbox onBlur="Search(this.value);"
id="PostSubject" runat="server" ... />
次に、JavaScript の Search を記述します。これによって、サーバー側の Search を呼び出します。 var oldValue = '';
function Search(value)
{
//一度検索したものは再度検索しないようにします
//特にユーザーが入力フィールドを行ったり来たりしている場合に注意してください
if (value != oldValue)
{
CreateEditPost.Search(value, Search_CallBack);
oldValue = value;
}
}
あとは応答を処理するだけです。前の例で、いくらか高度な方法を使用して表に結果を表示しているので、ここでは、単に動的な HTML を作成し、架空の DIV に挿入します。 function Search_CallBack(response)
{
//検索結果がない場合には、検索機能によって自動的にリダイレクトされるので、
//response.error を信用してはいけません
var results = response.value;
//検索結果がない場合
if (results == null)
{
return;
}
//検索結果を置く div
var someDiv = document.getElementById("someDiv");
var html = "";
for (var i = 0; i < results.length; ++i)
{
var result = results[i];
html += "<a target=_blank href='" + result.PostID
html += "/ShowPost.aspx'>";
html += result.Subject;
html += "</a><br />"
}
someDiv.innerHTML = html;
}
CommunityServer アプリケーションの 3 つのファイル (加えて、設定用の web.config) に小さな変更を加えるだけで、かなり巧みな機能を追加できました。ただし、既存アプリケーションに AJAX 対応の機能を簡単に追加するときは注意が必要です。実際の検索を実行している既存の ForumSearch クラスは、この例で紹介した形式で使用するように設計されていない場合があります。コードの変更によって、多数の追加の検索が発生し、大きな影響を及ぼす可能性があります。
AJAX を効果的に利用する
アプリケーションへの AJAX の適合は、その方法、場所、および既存アプリケーションであるか否かに応じて、状況ごとに大きく異なります。この記事では、Ajax.NET を使用して、AJAX に対応したソリューションを簡単に作成できることを確認しましたが、他にも考慮すべき事項があります。重大な検討事項は、アプリケーションの総合的なアーキテクチャおよび保守性への影響です。AJAX を使用すると、システムの層の区別がさらに不明瞭になります。特に、プレゼンテーション層、プレゼンテーション ロジック層、およびビジネス層で顕著です。これは AJAX 自体の問題ではなく、ユーザーの利用方法の問題です。層の境界が容易にくずれることを認識し、それを計算に入れた上で初めて実行する場合は、すべてがうまくいきます。
AJAX を使用すると、アプリケーションの保守が難しくなるでしょうか。その答えは、既存の JavaScript の使用量や、整理と保守についての力量に大きく左右されます。多くの開発者は、JavaScript の記述、テスト、およびデバッグが困難であると感じていますが、それは JavaScript 自体によるものではなく、サポート ツールや開発者の知識に原因があります。現在 JavaScript を使用してリンク付きのドロップダウン リストを実装しているユーザーが、AJAX に切り替えた場合は、コードを保守しやすくなる可能性が高いと言えます。その主な理由は、.NET の型および配列に対する Ajax.NET のサポートです。ただし、ポストバック処理を使用している場合は、まったく新しい言語 (JavaScript) をアプリケーションに導入することになり、ViewState に加わっていない一部のデータを処理する必要が生じます (ボタン クリック イベントで確認済み)。
もう 1 つの懸念は、Web サイトの使いやすさへの AJAX の影響です。AJAX の究極の機能は応答性の高いインターフェイスの作成ですが、開発者は次の 2 点を考慮する必要があります。第一に、また当然ですが、AJAX は JavaScript に依存します。誰もが知っているとおり、JavaScript を無効にするユーザーもいます。一部の基準では、JavaScript の使用または不使用にかかわらず Web サイトが動作することを要求しています (カナダ版米国リハビリテーション法508条など)。したがって、AJAX が機能していることを前提にしないでください。また、AJAX が使用可能でない場合は、アプリケーションをより一般的な Web 処理に戻す必要があります。次に、AJAX アプリケーションは (優れているにもかかわらず)、通常のアプリケーションの使用方法に慣れているユーザーにとって、不慣れな環境となる場合があります。たとえば、AJAX を通じてさまざまな処理を実行するページでは、[戻る] ボタン、[お気に入り] メニュー、およびその他のブラウザ機能が、ユーザーの考えているとおりに動作しない場合があります。
まとめ
AJAX は、これから登場する話題となっているテクノロジだけではありません。Web アプリケーションを構築するとき、日々の問題に対する代替ソリューションとなり得る明確なフレームワークです。Ajax.NET により、ASP.NET の開発者は AJAX をいっそう受け入れやすくなります。この記事で説明した 3 つの例、およびダウンロードして入手できるプロジェクトは、AJAX と Ajax.NET の使い方を理解するために提供されており、また独自のアイデアを試す簡単な練習の場にもなります。AJAX は、素晴らしいアプリケーションを作成するだけではありません。実際に顧客満足度を向上し、競争で優位に立つために利用できます。Atlas について論じられている一部の高度な概念によって、飛躍的に向上した製品が出荷される可能性があります。個人的には、これまでに見た AJAX の一番の実装は、かなり軽量で手ごろな規模のものでした。ユーザー独自の実装も、同等に好ましいユーザー エクスペリエンスを提供する必要があります。ただし、問題が発生したとき、AJAX だけが使用可能なソリューションではないこと、および最善策とはならない場合もあることに注意してください。それでは、他に比べようもないすばらしい ASP.NET コミュニティとなるよう、悪条件を取り除きましょう。
執筆者紹介
Karl Seguin は、活動の拠点を Microsoft ASP.NET ニュースグループに置き、開発者から寄せられる質問への回答や、執筆のテーマとなるトピックの収集を行っています。ニュースグループでの活動をしていないときは、もっぱら、World of Warcraft に熱中しています。
|