AJAX を使用し、動的更新を配信する
提供元: Microsoft
これは、ASP.NET MVC 1 を使用して小規模で完全な Web アプリケーションを構築する方法を説明する無料の "NerdDinner" アプリケーション チュートリアルの手順 10 です。
手順 10 では、ディナーの詳細ページ内に組み込まれている Ajax ベースのアプローチを使って、ログインしているユーザーがディナーへの参加に関心があると返事するためのサポートを実装します。
ASP.NET MVC 3 を使用している場合は、MVC 3 の概要または MVC Music Store に関するチュートリアルに従うことをお勧めします。
NerdDinner 手順 10: RSVP の受け取りを実現する AJAX
ここでは、ログインしているユーザーがディナーへの参加に関心があると返事するためのサポートを実装しましょう。 ディナーの詳細ページ内に組み込まれている AJAX ベースのアプローチを使って、これを実現します。
ユーザーが返事済みであるかどうかを示す
ユーザーは、/Dinners/Details/[id] という URL にアクセスして、特定のディナーに関する詳細を表示できます。
Details() アクション メソッドは次のように実装されます。
//
// GET: /Dinners/Details/2
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (dinner == null)
return View("NotFound");
else
return View(dinner);
}
RSVP のサポートを実装するための最初の手順は、(前に作成した Dinner.cs 部分クラス内の) Dinner オブジェクトに "IsUserRegistered(username)" というヘルパー メソッドを追加することです。 このヘルパー メソッドは、ユーザーがその Dinner に対して現在返事済みであるかどうかに応じて、true または false を返します。
public partial class Dinner {
public bool IsUserRegistered(string userName) {
return RSVPs.Any(r => r.AttendeeName.Equals(userName, StringComparison.InvariantCultureIgnoreCase));
}
}
次に、Details.aspx ビュー テンプレートに以下のコードを追加して、ユーザーがそのイベントに登録されているかどうかを示す適切なメッセージを表示します。
<% if (Request.IsAuthenticated) { %>
<% if (Model.IsUserRegistered(Context.User.Identity.Name)) { %>
<p>You are registred for this event!</p>
<% } else { %>
<p>You are not registered for this event</p>
<% } %>
<% } else { %>
<a href="/Account/Logon">Logon</a> to RSVP for this event.
<% } %>
これで、ユーザーが登録済みの Dinner にアクセスすると、次のメッセージが表示されます。
また、登録していない Dinner にアクセスすると、次のメッセージが表示されます。
Register アクション メソッドの実装
次に、ユーザーがディナーの詳細ページから出席できるようにするために必要な機能を追加しましょう。
これを実装するには、\Controllers ディレクトリを右クリックし、[追加] > [コントローラー] メニュー コマンドの順に選択して、新しい "RSVPController" クラスを作成します。
新しい RSVPController クラス内に "Register" アクション メソッドを実装します。このメソッドは、Dinner の ID を引数として受け取り、適切な Dinner オブジェクトを取得し、その登録済みユーザーのリストにログインしているユーザーが現在含まれているかどうかを確認して、含まれていない場合はその RSVP オブジェクトを追加します。
public class RSVPController : Controller {
DinnerRepository dinnerRepository = new DinnerRepository();
//
// AJAX: /Dinners/RSVPForEvent/1
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsUserRegistered(User.Identity.Name)) {
RSVP rsvp = new RSVP();
rsvp.AttendeeName = User.Identity.Name;
dinner.RSVPs.Add(rsvp);
dinnerRepository.Save();
}
return Content("Thanks - we'll see you there!");
}
}
アクション メソッドの出力として、シンプルな文字列を返している方法に注目してください。 このメッセージはビュー テンプレート内に埋め込むこともできましたが、短いメッセージなので、ここでは単にコントローラーの基底クラスの Content() ヘルパー メソッドを使って上記のような文字列メッセージを返しています。
AJAX を使った RSVPForEvent アクション メソッドの呼び出し
AJAX を使って、Details ビューから Register アクション メソッドを呼び出します。 その実装は非常に簡単です。 まず、次の 2 つのスクリプト ライブラリ参照を追加します。
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
最初のライブラリは、ASP.NET AJAX クライアント側スクリプト ライブラリのコアを参照しています。 このファイルはサイズが約 24k (圧縮済み) で、クライアント側のコア AJAX 機能が含まれています。 2 つ目のライブラリには、ASP.NET MVC の組み込み AJAX ヘルパー メソッド (まもなく使用します) と統合されるユーティリティ関数が含まれています。
次に、先ほど追加したビュー テンプレートのコードを更新して、"You are not registered for this event" (このイベントに登録されていません) というメッセージを出力する代わりにリンクをレンダリングします。このリンクを選択すると AJAX 呼び出しが実行され、RSVP コントローラーに対して RSVPForEvent アクション メソッドが呼び出されて、ユーザーの RSVP が処理されます。
<div id="rsvpmsg">
<% if(Request.IsAuthenticated) { %>
<% if(Model.IsUserRegistered(Context.User.Identity.Name)) { %>
<p>You are registred for this event!</p>
<% } else { %>
<%= Ajax.ActionLink( "RSVP for this event",
"Register", "RSVP",
new { id=Model.DinnerID },
new AjaxOptions { UpdateTargetId="rsvpmsg"}) %>
<% } %>
<% } else { %>
<a href="/Account/Logon">Logon</a> to RSVP for this event.
<% } %>
</div>
上記で使用した Ajax.ActionLink() ヘルパー メソッドは ASP.NET MVC に組み込まれており、Html.ActionLink() ヘルパー メソッドに似ていますが、標準ナビゲーションを実行する代わりに、リンクがクリックされたときにアクション メソッドに対する AJAX 呼び出しを行います。 上記では、"RSVP" コントローラーに対して "Register" アクション メソッドを呼び出し、DinnerID を "id" パラメーターとして渡しています。 最後に渡している AjaxOptions パラメーターは、アクション メソッドから返されたコンテンツを取得し、ID が "rsvpmsg" であるページ上の HTML <div> 要素を更新することを示しています。
これで、ユーザーがまだ登録していないディナーを参照すると、それに出席するためのリンクが表示されます。
"RSVP for this event" (このイベントに出席する) リンクをクリックすると、RSVP コントローラーの Register アクション メソッドに対して AJAX 呼び出しが行われ、それが完了すると次のように更新されたメッセージが表示されます。
この AJAX 呼び出しを行う際に要求されるネットワーク帯域幅とトラフィックはごくわずかです。 ユーザーが "RSVP for this event" (このイベントに出席する) リンクをクリックすると、URL /Dinners/Register/1 に対して小さな HTTP POST ネットワーク要求が行われます (ネットワーク上では次のようになります)。
POST /Dinners/Register/49 HTTP/1.1
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Referer: http://localhost:8080/Dinners/Details/49
Register アクション メソッドからの応答はシンプルです。
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 29
Thanks - we'll see you there!
この軽量の呼び出しは高速であり、低速なネットワークでも機能します。
jQuery アニメーションの追加
実装した AJAX 機能は適切かつ高速に動作します。 ただし、処理が高速すぎて、RSVP リンクが新しいテキストに置き換えられていることにユーザーが気付かない可能性があります。 結果をもう少しわかりやすくするために、シンプルなアニメーションを追加して更新メッセージに注意を向けさせることができます。
既定の ASP.NET MVC プロジェクト テンプレートには、jQuery が含まれています。これはすばらしい (そして非常に人気の) オープンソース JavaScript ライブラリであり、Microsoft でもサポートされています。 jQuery には、優れた HTML DOM 選択や効果ライブラリなど、さまざまな機能が用意されています。
jQuery を使うには、最初にそのスクリプト参照を追加します。 jQuery はサイト内のさまざまな場所で使うことになるので、そのスクリプト参照を Site.master マスター ページ ファイル内に追加して、すべてのページで使用できるようにします。
<script src="/Scripts/jQuery-1.3.2.js" type="text/javascript"></script>
jQuery を使って記述するコードでは、CSS セレクターを使って 1 つ以上の HTML 要素を取得する、グローバルな "$()" JavaScript メソッドを使うことがよくあります。 たとえば、$("#rsvpmsg") では ID が rsvpmsg である HTML 要素を、$(".something") では CSS クラス名が "something" であるすべての要素を選択できます。 "オンであるすべてのラジオ ボタンを返す" といったより高度なクエリを作成することもできます。その場合は $("input[@type=radio][@checked]") のようなセレクター クエリを使います。
要素を選択したら、それらに対してメソッドを呼び出してアクションを実行できます。たとえば、非表示にする場合: $("#rsvpmsg").hide();
RSVP のシナリオでは、"rsvpmsg" <div> を選択し、そのテキスト コンテンツのサイズをアニメーション化する、"AnimateRSVPMessage" という名前の単純な JavaScript 関数を定義します。 次のコードでは、テキストを最初は小さく表示し、400 ミリ秒の時間枠で徐々に拡大させます。
<script type="text/javascript">
function AnimateRSVPMessage() {
$("#rsvpmsg").animate({fontSize: "1.5em"},400);
}
</script>
次に、この JavaScript 関数を組み込んで AJAX 呼び出しが正常に完了した後に呼び出されるようにします。そのために、関数の名前を Ajax.ActionLink() ヘルパー メソッドに (AjaxOptions の "OnSuccess" イベント プロパティを使って) 渡します。
<%= Ajax.ActionLink( "RSVP for this event",
"Register", "RSVP",
new { id=Model.DinnerID },
new AjaxOptions { UpdateTargetId="rsvpmsg",
OnSuccess="AnimateRSVPMessage"}) %>
これで、"RSVP for this event" (このイベントに出席する) リンクをクリックして AJAX 呼び出しが正常に完了すると、返されるコンテンツ メッセージはアニメーション化されて大きくなります。
AjaxOptions オブジェクトは、"OnSuccess" イベントだけでなく OnBegin、OnFailure、OnComplete の各イベントも提供しており、これらを処理することもできます (他にもさまざまなプロパティや便利なオプションがあります)。
クリーンアップ - RSVP 部分ビューのリファクタリング
詳細ビュー テンプレートは少し長くなってきているので、時間が経つと理解しにくくなるおそれがあります。 コードを読みやすくするために、Details ページのすべての RSVP ビュー コードをカプセル化する部分ビュー (RSVPStatus.ascx) を作成して仕上げましょう。
これを行うには、\Views\Dinners フォルダーを右クリックし、[追加] > [ビュー] メニュー コマンドを選択します。 ここでは、厳密に型指定された ViewModel として Dinner オブジェクトを受け取るようにします。 その後、Details.aspx ビューから RSVP コンテンツをコピーして貼り付けることができます。
完了したら、Edit と Delete のリンク ビュー コードをカプセル化する別の部分ビュー (EditAndDeleteLinks.ascx) も作成しましょう。 これも、厳密に型指定された ViewModel として Dinner オブジェクトを受け取るように設定し、Details.aspx ビューから Edit と Delete のロジックをコピーして貼り付けます。
その後、詳細ビュー テンプレートの下部に 2 つの Html.RenderPartial() メソッド呼び出しを追加することができます。
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent"runat="server">
<%= Html.Encode(Model.Title) %>
</asp:Content>
<asp:Content ID="details" ContentPlaceHolderID="MainContent" runat="server">
<div id="dinnerDiv">
<h2><%=Html.Encode(Model.Title) %></h2>
<p>
<strong>When:</strong>
<%=Model.EventDate.ToShortDateString() %>
<strong>@</strong>
<%=Model.EventDate.ToShortTimeString() %>
</p>
<p>
<strong>Where:</strong>
<%=Html.Encode(Model.Address) %>,
<%=Html.Encode(Model.Country) %>
</p>
<p>
<strong>Description:</strong>
<%=Html.Encode(Model.Description) %>
</p>
<p>
<strong>Organizer:</strong>
<%=Html.Encode(Model.HostedBy) %>
(<%=Html.Encode(Model.ContactPhone) %>)
</p>
<% Html.RenderPartial("RSVPStatus"); %>
<% Html.RenderPartial("EditAndDeleteLinks"); %>
</div>
</asp:Content>
これでコードが読みやすく、保守しやすくなります。
次の手順
AJAX をさらに使用して、アプリケーションに対話型マッピングのサポートを追加する方法を見てみましょう。