次の方法で共有


繰り返し #7 – Ajax 機能を追加する (VB)

提供元: Microsoft

コードのダウンロード

7 番目のイテレーションでは、Ajax のサポートを追加することで、アプリケーションの応答性とパフォーマンスを向上させます。

連絡先管理の ASP.NET MVC アプリケーションをビルドする (VB)

この一連のチュートリアルでは、連絡先管理アプリケーション全体を最初から最後までビルドします。 連絡先マネージャー アプリケーションを使用すると、人物の一覧に連絡先情報 (名前、電話番号、電子メール アドレス) を保存できます。

複数のイテレーションを通してアプリケーションをビルドします。 イテレーションのたびに、アプリケーションを徐々に改善します。 複数のイテレーションからなるこのアプローチの目的は、お客様が各変更の理由を理解できるようにすることです。

  • イテレーション #1 - アプリケーションを作成します。 最初のイテレーションでは、可能な限り簡単な方法で連絡先マネージャーを作成します。 基本的なデータベース操作 (生成、読み取り、更新、削除 (CRUD)) のサポートを追加します。

  • イテレーション #2 - アプリケーションの外観を良くします。 このイテレーションでは、既定の ASP.NET MVC ビュー マスター ページとカスケード スタイル シートを変更することで、アプリケーションの外観を向上させます。

  • イテレーション #3 - フォームの検証を追加します。 3 番目のイテレーションでは、基本的なフォームの検証を追加します。 必須のフォーム フィールドを入力しないとユーザーがフォームを送信できないようにします。 また、メール アドレスと電話番号も検証します。

  • イテレーション #4 - アプリケーションを疎結合します。 この 4 番目のイテレーションでは、いくつかのソフトウェア デザイン パターンを利用して、連絡先マネージャー アプリケーションを簡単に維持し、変更できるようにします。 たとえば、Repository パターンと Dependency Injection パターンを使用するようにアプリケーションをリファクターします。

  • イテレーション #5 - 単体テストを作成します。 5 番目のイテレーションでは、単体テストを追加することで、アプリケーションを簡単に維持し、変更できるようにします。 データ モデル クラスをモックし、コントローラーと検証ロジックの単体テストをビルドします。

  • イテレーション #6 - テスト駆動開発を使用します。 この 6 番目のイテレーションでは、最初に単体テストを記述し、この単体テストに対してコードを記述することにより、新しい機能をアプリケーションに追加します。 このイテレーションでは、連絡先グループを追加します。

  • イテレーション #7 - Ajax 機能を追加します。 7 番目のイテレーションでは、Ajax のサポートを追加することで、アプリケーションの応答性とパフォーマンスを向上させます。

このイテレーション

Contact Manager アプリケーションのこのイテレーションでは、Ajax を使用するようにアプリケーションをリファクタリングします。 Ajax を利用することで、アプリケーションの応答性を高めることができます。 ページ内の特定の領域のみを更新する必要がある場合は、ページ全体をレンダリングするのを回避できます。

ユーザーが新しい連絡先グループを選択するたびにページ全体を再表示する必要がないように、インデックス ビューをリファクタリングします。 代わりに、他のユーザーが連絡先グループをクリックすると、連絡先の一覧が更新され、ページの他の部分はそのまま残ります。

また、削除リンクの動作方法も変更します。 別の確認ページを表示する代わりに、JavaScript の確認ダイアログが表示されます。 連絡先を削除することを確認すると、サーバーに対して HTTP DELETE 操作が実行され、データベースから連絡先レコードが削除されます。

さらに、jQuery を利用して、インデックス ビューにアニメーション効果を追加します。 新しい連絡先の一覧がサーバーからフェッチされるときにアニメーションを表示します。

最後に、ブラウザー履歴を管理するために ASP.NET AJAX フレームワークのサポートを利用します。 連絡先の一覧を更新するために Ajax 呼び出しを実行するたびに履歴ポイントを作成します。 こうすることで、ブラウザーの [戻る] ボタンと [進む] ボタンが機能します。

Ajax を使用する理由

Ajax を使用すると、多くの利点があります。 まず、アプリケーションに Ajax 機能を追加すると、ユーザー エクスペリエンスが向上します。 通常の Web アプリケーションでは、ユーザーがアクションを実行するたびに、ページ全体をサーバーにポストバックする必要があります。 アクションを実行するたびにブラウザーがロックされ、ページ全体がフェッチされて再表示されるまでユーザーは待機する必要があります。

デスクトップ アプリケーションの場合、これは許容しがたいエクスペリエンスになります。 しかし、従来は、Web アプリケーションの場合、この好ましくないエクスペリエンスを受け入れざるを得ませんでした。というのも、このエクスペリエンスを改良できる方法があることを知らなかったからです。 これについては Web アプリケーションの限界だと考えていましたが、実際は私たちの想像力の限界に過ぎませんでした。

Ajax アプリケーションでは、ページを更新するためだけにユーザー エクスペリエンスを停止させる必要はありません。 停止させる代わりに、バックグラウンドで非同期要求を実行してページを更新できます。 ページの一部が更新されるまでユーザーを待機させることはありません。

Ajax を利用することで、アプリケーションのパフォーマンスを向上させることもできます。 Ajax 機能を利用しない Contact Manager アプリケーションが現在どのように動作するかを考慮してみてください。 連絡先グループをクリックすると、インデックス ビュー全体を再表示する必要があります。 連絡先の一覧と連絡先グループの一覧は、データベース サーバーから取得する必要があります。 このデータはすべて、Web サーバーから Web ブラウザーにネットワーク経由で渡される必要があります。

ただし、アプリケーションに Ajax 機能を追加すると、ユーザーが連絡先グループをクリックしたときにページ全体が再表示されるのを避けることができます。 データベースから連絡先グループを取得する必要がなくなりました。 また、インデックス ビュー全体をネットワーク経由でプッシュする必要もありません。 Ajax を利用することで、データベース サーバーが実行する必要がある作業量を減らし、アプリケーションに必要なネットワーク トラフィックの量を減らすことができます。

Ajax を恐れないでください

ダウンレベルのブラウザーを心配して Ajax の使用を避ける開発者もいます。 このような開発者は、JavaScript をサポートしていないブラウザーからアクセスしても、Web アプリケーションが引き続き機能することを確認したいと考えています。 Ajax は JavaScript に依存しているため、Ajax の使用を避ける開発者もいます。

しかし、Ajax の実装方法に注意すれば、アップレベル ブラウザーとダウンレベル ブラウザーの両方で動作するアプリケーションを構築できます。 Contact Manager アプリケーションは、JavaScript をサポートしているブラウザーとサポートしていないブラウザーの両方で動作します。

JavaScript をサポートしているブラウザーで Contact Manager アプリケーションを使用すると、ユーザー エクスペリエンスが向上します。 たとえば、連絡先グループをクリックすると、連絡先を表示するページの領域のみが更新されます。

一方、JavaScript をサポートしていない (または JavaScript が無効にしている) ブラウザーで Contact Manager アプリケーションを使用する場合は、少し望ましくないユーザー エクスペリエンスが若干低下します。 たとえば、連絡先グループをクリックすると、一致する連絡先の一覧を表示するために、インデックス ビュー全体をブラウザーにポストバックする必要があります。

必要な JavaScript ファイルの追加

アプリケーションに Ajax 機能を追加するには、 3 つの JavaScript ファイルを使用する必要があります。 これらの 3 つのファイルはすべて、新しい ASP.NET MVC アプリケーションの Scripts フォルダーに含まれています。

アプリケーション内の複数のページで Ajax を使用する場合は、アプリケーションのビュー マスター ページに必要な JavaScript ファイルを含めるのが理にかなっています。 そうすることで、JavaScript ファイルがアプリケーション内のすべてのページに自動的に含まれます。

ビュー マスター ページの <head> タグ内に、次の JavaScript を追加します。

<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
    <script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery-1.2.6.min.js" type="text/javascript"></script>

Ajax を使用するようにインデックス ビューをリファクタリングする

まず、連絡先グループをクリックすると、連絡先を表示するビューの領域のみが更新されるように、インデックス ビューを変更します。 図 1 の赤いボックスには、更新する領域が含まれています。

Updating only contacts

図 01: 連絡先のみを更新する (クリックするとフルサイズの画像が表示されます)

最初の手順では、非同期的に更新するビューの部分を別の部分 (ビュー ユーザー コントロール) に分離します。 連絡先のテーブルを表示するインデックス ビューのセクションは、リスト 1 の部分に移動されました。

リスト 1 - Views\Contact\ContactList.ascx

<%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl(Of ContactManager.Group)" %>
<table class="data-table" cellpadding="0" cellspacing="0">
    <thead>
        <tr>
            <th class="actions edit">
                Edit
            </th>
            <th class="actions delete">
                Delete
            </th>
            <th>
                Name
            </th>
            <th>
                Phone
            </th>
            <th>
                Email
            </th>
        </tr>
    </thead>
    <tbody>
        <% For Each item in Model.Contacts %>
        <tr>
            <td class="actions edit">
                <a href='<%= Url.Action("Edit", New With {.id=item.Id}) %>'><img src="../../Content/Edit.png" alt="Edit" /></a>
            </td>
            <td class="actions delete">
                <a href='<%= Url.Action("Delete", New With {.id=item.Id}) %>'><img src="../../Content/Delete.png" alt="Delete" /></a>
            </td>
            <th>
                <%= Html.Encode(item.FirstName) %>
                <%= Html.Encode(item.LastName) %>
            </th>
            <td>
                <%= Html.Encode(item.Phone) %>
            </td>
            <td>
                <%= Html.Encode(item.Email) %>
            </td>
        </tr>
        <% Next %>
    </tbody>
</table>

リスト 1 の部分には、インデックス ビューとは異なるモデルがあることに注意してください。 <%@ Page %> ディレクティブの Inherits 属性は、部分が ViewUserControl <グループ> クラスから継承することを指定します。

更新されたインデックス ビューは、リスト 2 に含まれています。

リスト 2 - Views\Contact\Index.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of ContactManager.IndexModel)" %>
<%@ Import Namespace="ContactManager" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<title>Index</title>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<ul id="leftColumn">
<% For Each item in Model.Groups %>
    <li <%= Html.Selected(item.Id, Model.SelectedGroup.Id) %>>
    <%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList"})%>
    </li>
<% Next %>
</ul>
<div id="divContactList">
    <% Html.RenderPartial("ContactList", Model.SelectedGroup) %>
</div>

<div class="divContactList-bottom"> </div>
</asp:Content>

リスト 2 の更新されたビューについては、注意すべきことが 2 点あります。 1 点目は、部分に移動されたすべてのコンテンツが Html.RenderPartial() の呼び出しに置き換えられている点です。 Html.RenderPartial() メソッドは、インデックス ビューが最初に要求されたときに最初の連絡先セットを表示するために呼び出されます。

2 点目は、連絡先グループの表示に使用される Html.ActionLink() が Ajax.ActionLink() に置き換えられている点です。 Ajax.ActionLink() は、次のパラメーターを使用して呼び出されます。

<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList"})%>

最初のパラメーターはリンクに表示するテキストを表し、2 番目のパラメーターはルート値を表し、3 番目のパラメーターは Ajax オプションを表します。 この場合、UpdateTargetId Ajax オプションを使用して、Ajax 要求の完了後に更新する HTML <div> タグを指定します。 <div> タグを新しい連絡先の一覧で更新します。

連絡先コントローラーの更新された Index() メソッドは、リスト 3 に含まれています。

リスト 3 - Controllers\ContactController.vb (Index メソッド)

Public Function Index(ByVal id As Integer?) As ActionResult
    ' Get selected group
    Dim selectedGroup = _service.GetGroup(id)
    if IsNothing(selectedGroup) Then
        Return RedirectToAction("Index", "Group")
    End If

    ' Normal Request
    if Not Request.IsAjaxRequest() Then
        Dim model As new IndexModel With { _
            .Groups = _service.ListGroups(), _
            .SelectedGroup = selectedGroup _
        }
        Return View("Index", model)
    End If

    ' Ajax Request
    return PartialView("ContactList", selectedGroup)
End Function

更新された Index() アクションは、条件付きで 2 つのうちいずれかを返します。 Index() アクションが Ajax 要求によって呼び出された場合、コントローラーは対象部分を返します。 それ以外の場合、Index() アクションはビュー全体を返します。

Ajax 要求によって呼び出されたとき、Index() アクションはそれほど多くのデータを返す必要はありません。 通常の要求のコンテキストでは、インデックス アクションは、すべての連絡先グループと選択した連絡先グループの一覧を返します。 Ajax 要求のコンテキストでは、Index() アクションは選択したグループのみを返します。 Ajax は、データベース サーバーでの作業を軽減できることを意味します。

変更されたインデックス ビューは、アップレベル ブラウザーとダウンレベル ブラウザーの両方で機能します。 連絡先グループをクリックすると、ブラウザーで JavaScript がサポートされている場合は、連絡先の一覧を含むビューの領域のみが更新されます。 一方、ブラウザーが JavaScript をサポートしていない場合は、ビュー全体が更新されます。

更新されたインデックス ビューには問題が 1 つあります。 連絡先グループをクリックしても、選択したグループは強調表示されません。 グループの一覧は、Ajax 要求中に更新される領域の外部に表示されるため、適切なグループは強調表示されません。 この問題は、次のセクションで修正します。

jQuery アニメーション効果の追加

通常、Web ページ内のリンクをクリックすると、ブラウザーの進行状況バーを使用して、ブラウザーが更新されたコンテンツをアクティブにフェッチしているかどうかを検出できます。 一方、Ajax 要求を実行すると、ブラウザーの進行状況バーには進行状況が表示されません。 これにより、ユーザーが神経質になる可能性があります。 ブラウザーがフリーズしたかどうかを確認するにはどうすればよいですか?

Ajax 要求の実行中に、作業が実行されていることをユーザーに示す方法はいくつかあります。 方法の一つとして、シンプルなアニメーションを表示することができます。 たとえば、Ajax 要求が開始されたときに領域をフェード アウトし、要求が完了したときにその領域をフェード インできます。

Microsoft ASP.NET MVC フレームワークに含まれている jQuery ライブラリを使用して、アニメーション効果を作成します。 更新されたインデックス ビューは、リスト 4 に含まれています。

リスト 4 - Views\Contact\Index.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of ContactManager.IndexModel)" %>
<%@ Import Namespace="ContactManager" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<title>Index</title>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<script type="text/javascript">

    function beginContactList(args) 
    {
        // Highlight selected group
        $('#leftColumn li').removeClass('selected');
        $(this).parent().addClass('selected');

        // Animate
        $('#divContactList').fadeOut('normal');
    }

    function successContactList() 
    {
        // Animate
        $('#divContactList').fadeIn('normal');
    }

    function failureContactList()
    {
        alert("Could not retrieve contacts.");
    }

</script>

<ul id="leftColumn">
<% For Each item in Model.Groups %>
    <li <%= Html.Selected(item.Id, Model.SelectedGroup.Id) %>>
    <%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" })%>
    </li>
<% Next %>
</ul>
<div id="divContactList">
    <% Html.RenderPartial("ContactList", Model.SelectedGroup) %>
</div>

<div class="divContactList-bottom"> </div>
</asp:Content>

更新されたインデックス ビューには、新しい JavaScript 関数が 3 つ含まれていることに注意してください。 最初の 2 つの関数では、新しい連絡先グループをクリックしたときに、連絡先の一覧をフェード アウトおよびフェード インするために jQuery を使用しています。 3 つ目の関数は、Ajax 要求でエラー (ネットワーク タイムアウトなど) が発生したときにエラー メッセージを表示します。

1 つ目の関数は、選択したグループを強調表示する処理も行います。 class = 選択した属性は、クリックされた要素の親要素 (LI 要素) に追加されます。 ここでも、jQuery を使用すると、適切な要素を選択し、簡単に CSS クラスを追加できます。

これらのスクリプトは、Ajax.ActionLink() AjaxOptions パラメーターを使用してグループ リンクに関連付けられています。 更新された Ajax.ActionLink() メソッド呼び出しは次のようになります。

<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" })%>

ブラウザー履歴のサポートの追加

通常、リンクをクリックしてページを更新すると、ブラウザーの履歴が更新されます。 そうすることで、ブラウザーの [戻る] ボタンをクリックすると、ページを前の状態に戻すことができます。 たとえば、[友達] 連絡先グループをクリックした後に、[ビジネス] 連絡先グループをクリックすると、ブラウザーの [戻る] ボタンをクリックして、[友達] 連絡先グループを選択したときのページの状態に戻ることができます。

残念ながら、Ajax 要求を実行しても、ブラウザーの履歴は自動的に更新されません。 連絡先グループをクリックすると、一致する連絡先の一覧が Ajax 要求で取得された場合、ブラウザーの履歴は更新されません。 新しい連絡先グループを選択した後、ブラウザーの [戻る] ボタンを使用しても元の連絡先グループに戻ることはできません。

ユーザーが Ajax 要求を実行した後にブラウザーの [戻る] ボタンを使用できるようにする場合は、もう少し作業を行う必要があります。 ASP.NET AJAX Framework に組み込まれているブラウザー履歴管理機能を利用する必要があります。

ASP.NET AJAX のブラウザー履歴では、次の 3 つの操作を行う必要があります。

  1. enableBrowserHistory プロパティを true に設定して、ブラウザー履歴を有効にします。
  2. addHistoryPoint() メソッドを呼び出して、ビューの状態が変化したときに履歴ポイントを保存します。
  3. navigate イベントが発生したときにビューの状態を再構築します。

更新されたインデックス ビューは、リスト 5 に含まれています。

リスト 5 - Views\Contact\Index.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of ContactManager.IndexModel)" %>
<%@ Import Namespace="ContactManager" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<title>Index</title>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<script type="text/javascript">

    var _currentGroupId = -1;

    Sys.Application.add_init(pageInit);

    function pageInit() {
        // Enable history
        Sys.Application.set_enableHistory(true);

        // Add Handler for history
        Sys.Application.add_navigate(navigate);
    }

    function navigate(sender, e) {
        // Get groupId from address bar
        var groupId = e.get_state().groupId;

        // If groupId != currentGroupId then navigate
        if (groupId != _currentGroupId) {
            _currentGroupId = groupId;
            $("#divContactList").load("/Contact/Index/" + groupId);
            selectGroup(groupId);
        }
    }

    function selectGroup(groupId) {
        $('#leftColumn li').removeClass('selected');
        if (groupId)
            $('a[groupid=' + groupId + ']').parent().addClass('selected');
        else
            $('#leftColumn li:first').addClass('selected');
    }

    function beginContactList(args) {
        // Highlight selected group
        _currentGroupId = this.getAttribute("groupid");
        selectGroup(_currentGroupId);

        // Add history point
        Sys.Application.addHistoryPoint({ "groupId": _currentGroupId });

        // Animate
        $('#divContactList').fadeOut('normal');
    }

    function successContactList() {
        // Animate
        $('#divContactList').fadeIn('normal');
    }

    function failureContactList() {
        alert("Could not retrieve contacts.");
    }

</script>

<ul id="leftColumn">
<% For Each item in Model.Groups %>
    <li <%= Html.Selected(item.Id, Model.SelectedGroup.Id) %>>
    <%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" }, New With { .groupid = item.Id })%>
    </li>
<% Next %>
</ul>
<div id="divContactList">
    <% Html.RenderPartial("ContactList", Model.SelectedGroup) %>
</div>

<div class="divContactList-bottom"> </div>
</asp:Content>

リスト 5 では、pageInit() 関数でブラウザー履歴が有効になっています。 また、pageInit() 関数は、navigate イベントのイベント ハンドラーを設定するためにも使用されます。 navigate イベントは、ブラウザーの [進む] または [戻る] ボタンによってページの状態が変更されるたびに発生します。

beginContactList() メソッドは、連絡先グループをクリックすると呼び出されます。 このメソッドは、addHistoryPoint() メソッドを呼び出して新しい履歴ポイントを作成します。 クリックした連絡先グループの ID が履歴に追加されます。

グループ ID は、連絡先グループ リンクの expando 属性から取得されます。 リンクは、Ajax.ActionLink() の次の呼び出しでレンダリングされます。

<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" }, New With { .groupid = item.Id })%>

Ajax.ActionLink() に渡される最後のパラメーターは、groupid という名前の expando 属性をリンク (XHTML 互換性のために小文字) に追加します。

ユーザーがブラウザーの [戻る] または [進む] ボタンをクリックすると、navigate イベントが発生し、navigate() メソッドが呼び出されます。 このメソッドは、navigate メソッドに渡されたブラウザー履歴ポイントに対応するページの状態と一致するように、ページに表示される連絡先を更新します。

Ajax 削除の実行

現在、連絡先を削除するには、[削除] リンクをクリックし、[削除の確認] ページに表示されている [削除] ボタンをクリックする必要があります (図 2 を参照)。 これは、データベースのレコードを削除するような簡単なことをするために、多くのページが要求されているように感じられます。

The delete confirmation page

図 02: 削除の確認ページ (クリックするとフルサイズの画像が表示されます)

削除の確認ページをスキップし、インデックス ビューから直接連絡先を削除したくなる場合があります。 しかし、このアプローチを使用すると、アプリケーションにセキュリティホールが開かれるので、この誘惑を避ける必要があります。 一般的に、Web アプリケーションの状態を変更するアクションを呼び出すときに、HTTP GET 操作を実行する必要はありません。 削除を実行する場合は、HTTP POST 操作、または HTTP DELETE 操作を実行する必要があります。

[削除] リンクは ContactList 部分に含まれています。 ContactList 部分の更新バージョンは、リスト 6 に含まれています。

リスト 6 - Views\Contact\ContactList.ascx

<%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl(Of ContactManager.Group)" %>
<%@ Import Namespace="ContactManager" %>
<table class="data-table" cellpadding="0" cellspacing="0">
    <thead>
        <tr>
            <th class="actions edit">
                Edit
            </th>
            <th class="actions delete">
                Delete
            </th>
            <th>
                Name
            </th>
            <th>
                Phone
            </th>
            <th>
                Email
            </th>
        </tr>
    </thead>
    <tbody>
        <% For Each item in Model.Contacts %>
        <tr>
            <td class="actions edit">
                <a href='<%= Url.Action("Edit", New With {.id=item.Id}) %>'><img src="../../Content/Edit.png" alt="Edit" /></a>
            </td>
            <td class="actions delete">
                <%= Ajax.ImageActionLink("../../Content/Delete.png", "Delete", "Delete", New with { .id = item.Id }, New AjaxOptions With { .Confirm = "Delete contact?", .HttpMethod = "Delete", .UpdateTargetId = "divContactList" })%> 
            </td>
            <th>
                <%= Html.Encode(item.FirstName) %>
                <%= Html.Encode(item.LastName) %>
            </th>
            <td>
                <%= Html.Encode(item.Phone) %>
            </td>
            <td>
                <%= Html.Encode(item.Email) %>
            </td>
        </tr>
        <% Next %>
    </tbody>
</table>

[削除] リンクは、Ajax.ImageActionLink() メソッドの次の呼び出しでレンダリングされます。

<%= Ajax.ImageActionLink("../../Content/Delete.png", "Delete", "Delete", New with { .id = item.Id }, New AjaxOptions With { .Confirm = "Delete contact?", .HttpMethod = "Delete", .UpdateTargetId = "divContactList" })%<

Note

Ajax.ImageActionLink() は、ASP.NET MVC フレームワークの標準的な部分ではありません。 Ajax.ImageActionLink() は、Contact Manager プロジェクトに含まれるカスタム ヘルパー メソッドです。

AjaxOptions パラメーターには 2 つのプロパティがあります。 まず、Confirm プロパティを使用して、ポップアップ JavaScript の確認ダイアログを表示します。 次に、HttpMethod プロパティを使用して HTTP DELETE 操作を実行します。

リスト 7 には、連絡先コントローラーに追加された新しい AjaxDelete() アクションが含まれています。

リスト 7 - Controllers\ContactController.vb (AjaxDelete)

<AcceptVerbs(HttpVerbs.Delete), ActionName("Delete")> _
Public Function AjaxDelete(ByVal id As Integer) As ActionResult
    ' Get contact and group
    Dim contactToDelete = _service.GetContact(id)
    Dim selectedGroup = _service.GetGroup(contactToDelete.Group.Id)

    ' Delete from database
    _service.DeleteContact(contactToDelete)

    ' Return Contact List
    Return PartialView("ContactList", selectedGroup)
End Function

AjaxDelete() アクションは AcceptVerbs 属性で修飾されます。 この属性は、HTTP DELETE 操作以外の HTTP 操作によって、アクションが呼び出されないようにします。 特に、HTTP GET ではこのアクションを呼び出すことはできません。

データベース レコードを削除した後、削除されたレコードを含まない更新された連絡先の一覧を表示する必要があります。 AjaxDelete() メソッドは、ContactList の一部と更新された連絡先の一覧を返します。

まとめ

このイテレーションでは、Contact Manager アプリケーションに Ajax 機能を追加しました。 Ajax を使用して、アプリケーションの応答性とパフォーマンスを向上させました。

最初に、連絡先グループをクリックしてもビュー全体が更新されないように、インデックス ビューをリファクタリングしました。 代わりに、連絡先グループをクリックすると、連絡先の一覧のみが更新されます。

次に、jQuery アニメーション効果を使用して、連絡先の一覧をフェード アウトしてフェード インしました。 Ajax アプリケーションにアニメーションを追加すると、アプリケーションのユーザーにブラウザーの進行状況バーと同等の機能を提供できます。

また、ブラウザー履歴のサポートを Ajax アプリケーションに追加しました。 ユーザーがブラウザーの [戻る] ボタンと [進む] ボタンをクリックして、インデックス ビューの状態を変更できるようにしました。

最後に、HTTP DELETE 操作をサポートする削除リンクを作成しました。 Ajax の削除を実行することで、ユーザーは追加の [削除の確認] ページを要求することなく、データベース レコードを削除できます。