ハンズ オン ラボ: ASP.NET Web API と Angular.js でシングル ページ アプリケーション (SPA) をビルドする

Web Camps チーム

Web Camps トレーニング キットのダウンロード

このハンズ オン ラボでは、ASP.NET 4.x の ASP.NET Web API と Angular.js を使用してシングル ページ アプリケーション (SPA) をビルドする方法について説明します。

このハンズ オン ラボでは、これらのテクノロジを利用して Geek Quiz (SPA の概念に基づいた Trivia Web サイト) を実装します。 まず、ASP.NET Web API を使用してサービス レイヤーを実装し、クイズの質問を取得して解答を保存するために必要なエンドポイントを公開します。 次に、AngularJS と CSS3 変換の効果を使用して、機能豊富で応答性の高い UI をビルドします。

従来の Web アプリケーションでは、クライアント (ブラウザー) はページを要求してサーバーとの通信を開始します。 その後、サーバーは要求を処理し、ページの HTML をクライアントに送信します。 ページとの以降の対話 (ユーザーがリンク先に移動する、データを含むフォームを送信するなど) では、新しい要求がサーバーに送信され、フローが再び開始されます。サーバーは要求を処理し、クライアントによって要求された新しいアクションに応答して新しいページをブラウザーに送信します。

シングル ページ アプリケーション (SPA) では、最初の要求の後にページ全体がブラウザーに読み込まれますが、それ以降の対話は Ajax 要求を介して行われます。 つまり、ブラウザーが更新する必要があるのは変更されたページの部分のみです。ページ全体を再読み込みする必要はありません。 SPA のアプローチにより、アプリケーションがユーザーの操作に応答するために要する時間が短縮され、より滑らかなエクスペリエンスが得られます。

SPA のアーキテクチャには、従来の Web アプリケーションにはない特定の課題が伴います。 しかし、ASP.NET Web API、AngularJS などの JavaScript フレームワーク、CSS3 によって提供される新しいスタイル機能などの新しいテクノロジにより、SPA の設計とビルドが大幅に簡単になります。

すべてのサンプル コードとスニペットは、Web Camps トレーニング キットに含まれています (https://aka.ms/webcamps-training-kit で入手可能)。

概要

目標

このハンズオン ラボでは、次のことを行う方法について説明します。

  • JSON データを送受信する ASP.NET Web API サービスを作成する
  • AngularJS を使用して応答性の高い UI を作成する
  • CSS3 変換を使用して UI エクスペリエンスを強化する

前提条件

このハンズオン ラボを完了するために必要なものは次のとおりです。

段取り

このハンズ オン ラボで演習を実行するには、まず環境を設定する必要があります。

  1. Windows エクスプローラーを開き、ラボの Source フォルダーを参照します。
  2. Setup.cmd を右クリックし、[管理者として実行] を選択して、環境を構成し、このラボ用の Visual Studio コード スニペットをインストールするセットアップ プロセスを起動します。
  3. [ユーザー アカウント制御] ダイアログ ボックスが表示されている場合は、続行するアクションを受け入れます。

Note

このラボに必要な依存関係をすべて確認したうえでセットアップを実行してください。

コード スニペットの使用

ラボ ドキュメントには、コード ブロックを挿入するように指示する記述が随所に出現します。 便宜上、このコードのほとんどは Visual Studio Code スニペットとして提供されており、手動による追加の必要なく、Visual Studio 2013 内からアクセスできます。

Note

各演習には、開始ソリューションが演習の Begin フォルダーに用意されています。これにより、各演習を他の演習とは別に実行できます。 演習中に追加されたコード スニペットは、これらの開始ソリューションには含まれていないので、演習を完了するまで機能しない可能性があることに注意してください。 演習のソース コード内には、対応する演習手順の実行結果から得られたコードを含む Visual Studio ソリューションを格納した End フォルダーもあります。 ハンズオン ラボを進める過程でわからない点が生じた場合は、これらのソリューションを参考にしてください。


演習

このハンズオン ラボに含まれる演習は次のとおりです。

  1. Web API を作成する
  2. SPA インターフェイスを作成する

このラボの推定所要時間: 60 分

Note

Visual Studio を初めて起動するときは、定義済みの設定コレクションのいずれかを選択する必要があります。 定義済みの各コレクションは特定の開発スタイルに合わせて設計されており、ウィンドウ レイアウト、エディターの動作、IntelliSense コード スニペット、ダイアログ ボックスのオプションが決まっています。 このラボの手順では、"全般的な開発設定" コレクションを使用する際に Visual Studio で特定の作業を行うために必要な操作について説明しています。 開発環境に別の設定コレクションを選択すると、手順に違いが生まれ、その点の考慮が必要になる場合があります。

演習 1: Web API を作成する

SPA の重要な部分の 1 つは、サービス レイヤーです。 これは、UI から送信された Ajax 呼び出しを処理し、その呼び出しへの応答としてデータを返す役割を担います。 取得したデータは、クライアントが解析して使用できるようにするために、コンピューターで読み取り可能な形式で表示する必要があります。

Web API フレームワークは、ASP.NET Stack の一部であり、HTTP サービスを簡単に実装できるように設計されています。一般的には、RESTful API を介して JSON 形式または XML 形式のデータを送受信します。 この演習では、Geek Quiz アプリケーションをホストする Web サイトを作成した後、バックエンド サービスを実装し、ASP.NET Web API を使用してクイズ データを公開、保持します。

タスク 1 - Geek Quiz の初期プロジェクトを作成する

このタスクでは、Visual Studio に付属する One ASP.NET プロジェクト タイプに基づいて、ASP.NET Web API をサポートする新しい ASP.NET MVC プロジェクトの作成を開始します。 One ASP.NET は、あらゆる ASP.NET テクノロジを統合し、それらを必要に応じて組み合わせるオプションを提供します。 その後、Entity Framework のモデル クラスとデータベース初期化子を追加して、クイズの質問を挿入します。

  1. Visual Studio Express 2013 for Web を開き、[ファイル] - [新しいプロジェクト] を選択して新しいソリューションを開始します。

    Creating a New Project

    新規プロジェクトの作成

  2. [新しいプロジェクト] ダイアログ ボックスで、[Visual C#] - [Web] タブの下にある [ASP.NET Web アプリケーション] を選択します。.NET Framework 4.5 が選択されていることを確認し、GeekQuiz という名前を付け、場所を選択して [OK] をクリックします。

    Creating a new ASP.NET Web Application project

    新しい ASP.NET Web アプリケーション プロジェクトの作成

  3. [新しい ASP.NET プロジェクト] ダイアログ ボックスで、MVC テンプレートを選択し、[Web API] オプションを選択します。 また、[認証] オプションが [個別のユーザー アカウント] に設定されていることを確認します。 OK をクリックして続行します。

    Creating a new project with the MVC template, including Web API components

    Web API コンポーネントを含む MVC テンプレートを使用して新しいプロジェクトを作成

  4. ソリューション エクスプローラーで、GeekQuiz プロジェクトの Models フォルダーを右クリックし、[追加] - [既存の項目] を選択します。

    Adding an existing item

    既存の項目の追加

  5. [既存項目の追加] ダイアログ ボックスで、Source/Assets/Models フォルダーに移動し、すべてのファイルを選択します。 追加をクリックします。

    Adding the model assets

    モデル資産の追加

    Note

    これらのファイルを追加することで、データ モデル、Entity Framework のデータベース コンテキスト、Geek Quiz アプリケーションのデータベース初期化子が追加されます。

    Entity Framework (EF) はオブジェクト リレーショナル マッパー (ORM) であり、リレーショナル ストレージ スキーマを使用して直接プログラミングするのではなく、概念的なアプリケーション モデルを使用してプログラミングすることで、データ アクセス アプリケーションを作成できます。 Entity Framework の詳細については、ここをご覧ください。

    次に示すのは、追加したクラスの説明です。

    • TriviaOption: クイズの質問に関連付けられた単一のオプションを表します。
    • TriviaQuestion: クイズの質問を表し、Options プロパティを使用して関連するオプションを公開します。
    • TriviaAnswer: クイズの質問に対する解答として、ユーザーが選択したオプションを表します。
    • TriviaContext: Geek Quiz アプリケーションの Entity Framework のデータベース コンテキストを表します。 このクラスは DContext から派生し、上記のエンティティのコレクションを表す DbSet プロパティを公開します。
    • TriviaDatabaseInitializer: CreateDatabaseIfNotExists から継承する TriviaContext クラスの Entity Framework 初期化子の実装。 このクラスの既定の動作では、データベースは存在しない場合にのみ作成され、Seed メソッドで指定されたエンティティが挿入されます。
  6. Global.asax.cs ファイルを開き、ステートメントを使用して以下を追加します。

    using GeekQuiz.Models;
    
  7. TriviaDatabaseInitializer をデータベース初期化子として設定するには、Application_Start メソッドの先頭に次のコードを追加します。

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            System.Data.Entity.Database.SetInitializer(new TriviaDatabaseInitializer()); 
    
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
    
  8. 認証済みユーザーへのアクセスを制限するように Home コントローラーを変更します。 これを行うには、Controllers フォルダー内の HomeController.cs ファイルを開き、Authorize 属性を HomeController クラス定義に追加します。

    namespace GeekQuiz.Controllers
    {
        [Authorize]
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
    
            ...
        }
    }
    

    Note

    Authorize フィルターは、ユーザーが認証されているかどうかを確認します。 ユーザーが認証されていない場合は、アクションを呼び出さずに HTTP 状態コード 401 (Unauthorized) を返します。 フィルターは、グローバル、コントローラー レベル、または個々のアクションのレベルで適用できます。

  9. 次に、Web ページとブランド化のレイアウトをカスタマイズします。 これを行うには、Views | Shared フォルダーの _Layout.cshtml ファイルを開き、My ASP.NET ApplicationGeek Quiz に置き換えることで <title> 要素の内容を更新します。

    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>@ViewBag.Title - Geek Quiz</title>
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    
    </head>
    
  10. 同じファイル内で、About リンクと Contact リンクを削除し、Home リンクの名前を Play に変更して、ナビゲーション バーを更新します。 さらに、Application name リンクの名前を Geek Quiz に変更します。 ナビゲーション バーの HTML は、次のコードのようになります。

    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Geek Quiz", "Index", "Home", null, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Play", "Index", "Home")</li>
                </ul>
                @Html.Partial("_LoginPartial")
            </div>
        </div>
    </div>
    
  11. My ASP.NET ApplicationGeek Quiz に置き換えて、レイアウト ページのフッターを更新します。 これを行うには、<footer> 要素の内容を次の強調表示されたコードに置き換えます。

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - Geek Quiz</p>
        </footer>
    </div>
    

タスク 2 - TriviaController Web API を作成する

前のタスクでは、Geek Quiz Web アプリケーションの初期構造を作成しました。 次に、クイズ データ モデルと対話し、次のアクションを公開する単純な Web API サービスをビルドします。

  • GET /api/trivia: 認証済みユーザーが答える次の質問をクイズ リストから取得します。
  • POST /api/trivia: 認証済みユーザーによって指定されたクイズの解答を格納します。

Visual Studio で提供される ASP.NET スキャフォールディング ツールを使用して、Web API コントローラー クラスのベースラインを作成します。

  1. App_Start フォルダー内のWebApiConfig.cs ファイルを開きます。 このファイルは、ルートを Web API コントローラー アクションにどのようにマッピングするかなど、Web API サービスの構成を定義します。

  2. ファイルの先頭に次の using ステートメントを追加します。

    using Newtonsoft.Json.Serialization;
    
  3. 次の強調表示されたコードを Register メソッドに追加して、Web API アクション メソッドによって取得された JSON データのフォーマッターをグローバルに構成します。

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
    
            // Use camel case for JSON data.
            config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    
            // Web API routes
            config.MapHttpAttributeRoutes();
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
    

    Note

    CamelCasePropertyNamesContractResolver は、プロパティ名をキャメル ケース (JavaScript でのプロパティ名の一般的な表記規則) に自動的に変換します。

  4. ソリューション エクスプローラーで、GeekQuiz プロジェクトの Controllers フォルダーを右クリックし、[追加] - [新規スキャフォールディング アイテム] の順に選択します。

    Creating a new scaffolded item

    新しいスキャフォールディング アイテムの作成

  5. [スキャフォールディングを追加] ダイアログ ボックスで、左側のウィンドウの [共通] ノードが選択されていることを確認します。 次に、中央のウィンドウで [Web API 2 Controller - Empty] テンプレートを選択し、[追加] をクリックします。

    Selecting the Web API 2 Controller Empty template

    Web API 2 Controller Empty テンプレートの選択

    Note

    ASP.NET スキャフォールディング は、ASP.NET Web アプリケーション用のコード生成フレームワークです。 Visual Studio 2013 には、MVC と Web API プロジェクト用にプレインストールされたコード ジェネレーターが含まれています。 標準データ操作の開発に必要な時間を短縮するために、データ モデルと対話するコードをすばやく追加する場合は、プロジェクトでスキャフォールディングを使用する必要があります。

    スキャフォールディング プロセスにより、必要なすべての依存関係がプロジェクトに確実にインストールされます。 たとえば、空の ASP.NET プロジェクトから開始し、スキャフォールディングを使用して Web API コントローラーを追加すると、必要な Web API NuGet パッケージと参照がプロジェクトに自動的に追加されます。

  6. [コントローラーの追加] ダイアログ ボックスで、[コントローラー名] テキスト ボックスに「TriviaController」と入力し、[追加] をクリックします。

    Adding the Trivia Controller

    Trivia コントローラーの追加

  7. その後、TriviaController.cs ファイルが、GeekQuiz プロジェクトの Controllers フォルダー (空の TriviaController クラスを含む) に追加されます。 ファイルの先頭に次の using ステートメントを追加します。

    (コード スニペット - AspNetWebApiSpa - Ex1 - TriviaControllerUsings)

    using System.Data.Entity;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http.Description;
    using GeekQuiz.Models;
    
  8. TriviaController クラスの先頭に次のコードを追加して、コントローラー内の TriviaContext インスタンスを定義、初期化、破棄します。

    (コード スニペット - AspNetWebApiSpa - Ex1 - TriviaControllerContext)

    public class TriviaController : ApiController
    {
        private TriviaContext db = new TriviaContext();
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.db.Dispose();
            }
    
            base.Dispose(disposing);
        }
    }
    

    Note

    TriviaControllerDispose メソッドは、TriviaContext インスタンスの Dispose メソッドを呼び出します。これにより、TriviaContext インスタンスが破棄またはガベージ コレクションされるときに、コンテキスト オブジェクトによって使用されるすべてのリソースが確実に解放されます。 これには、Entity Framework によって開かれたすべてのデータベース接続を閉じることが含まれます。

  9. TriviaController クラスの末尾に次のヘルパー メソッドを追加します。 このメソッドは、指定したユーザーが解答する、次のクイズの質問をデータベースから取得します。

    (コード スニペット - AspNetWebApiSpa - Ex1 - TriviaControllerNextQuestion)

    private async Task<TriviaQuestion> NextQuestionAsync(string userId)
    {
        var lastQuestionId = await this.db.TriviaAnswers
            .Where(a => a.UserId == userId)
            .GroupBy(a => a.QuestionId)
            .Select(g => new { QuestionId = g.Key, Count = g.Count() })
            .OrderByDescending(q => new { q.Count, QuestionId = q.QuestionId })
            .Select(q => q.QuestionId)
            .FirstOrDefaultAsync();
    
        var questionsCount = await this.db.TriviaQuestions.CountAsync();
    
        var nextQuestionId = (lastQuestionId % questionsCount) + 1;
        return await this.db.TriviaQuestions.FindAsync(CancellationToken.None, nextQuestionId);
    }
    
  10. 次の Get アクション メソッドを TriviaController クラスに追加します。 このアクション メソッドは、前の手順で定義した NextQuestionAsync ヘルパー メソッドを呼び出して、認証済みユーザーの次の質問を取得します。

    (コード スニペット - AspNetWebApiSpa - Ex1 - TriviaControllerGetAction)

    // GET api/Trivia
    [ResponseType(typeof(TriviaQuestion))]
    public async Task<IHttpActionResult> Get()
    {
        var userId = User.Identity.Name;
    
        TriviaQuestion nextQuestion = await this.NextQuestionAsync(userId);
    
        if (nextQuestion == null)
        {
            return this.NotFound();
        }
    
        return this.Ok(nextQuestion);
    }
    
  11. TriviaController クラスの末尾に次のヘルパー メソッドを追加します。 このメソッドは、指定された解答をデータベースに格納し、解答が正しいかどうかを示すブール値を返します。

    (コード スニペット - AspNetWebApiSpa - Ex1 - TriviaControllerStoreAsync)

    private async Task<bool> StoreAsync(TriviaAnswer answer)
    {
        this.db.TriviaAnswers.Add(answer);
    
        await this.db.SaveChangesAsync();
        var selectedOption = await this.db.TriviaOptions.FirstOrDefaultAsync(o => o.Id == answer.OptionId
            && o.QuestionId == answer.QuestionId);
    
        return selectedOption.IsCorrect;
    }
    
  12. 次の Post アクション メソッドを TriviaController クラスに追加します。 このアクション メソッドは、解答と認証済みユーザーを関連付け、StoreAsync ヘルパー メソッドを呼び出します。 次に、ヘルパー メソッドによって返されたブール値を含む応答を送信します。

    (コード スニペット - AspNetWebApiSpa - Ex1 - TriviaControllerPostAction)

    // POST api/Trivia
    [ResponseType(typeof(TriviaAnswer))]
    public async Task<IHttpActionResult> Post(TriviaAnswer answer)
    {
        if (!ModelState.IsValid)
        {
            return this.BadRequest(this.ModelState);
        }
    
        answer.UserId = User.Identity.Name;
    
        var isCorrect = await this.StoreAsync(answer);
        return this.Ok<bool>(isCorrect);
    }
    
  13. TriviaController クラス定義に Authorize 属性を追加して、認証済みユーザーへのアクセスを制限するように Web API コントローラーを変更します。

    [Authorize]
    public class TriviaController : ApiController
    {
        ...
    }
    

タスク 3 - ソリューションを実行する

このタスクでは、前のタスクで作成した Web API サービスが想定どおりに動作していることを確認します。 Internet Explorer F12 Developer Tools を使用して、ネットワーク トラフィックをキャプチャし、Web API サービスからの応答全体を検査します。

Note

Visual Studio ツール バーの [スタート] ボタンで Internet Explorer が選択されていることを確認します。

Internet Explorer option

  1. F5 キーを押して、ソリューションを実行します。 [ログイン] ページがブラウザーに表示されます。

    Note

    アプリケーションが起動すると、既定の MVC ルートがトリガーされ、既定では HomeController クラスの Index アクションにマップされます。 HomeController は認証済みユーザーに制限されていますが (演習 1 で Authorize 属性を使用してこのクラスを修飾)、まだ認証済みユーザーがいないため、アプリケーションは元の要求をログイン ページにリダイレクトします。

    Running the solution

    ソリューションの実行

  2. [登録] をクリックして新規ユーザーを作成します。

    Registering a new user

    新規ユーザーの登録

  3. [登録] ページで、[ユーザー名][パスワード] を入力し、[登録] をクリックします。

    Register page

    [登録] ページ

  4. アプリケーションによって新しいアカウントが登録され、ユーザーが認証され、ホーム ページにリダイレクトされます。

    User is authenticated

    ユーザーが認証される

  5. ブラウザーで、F12 キーを押して [開発者ツール] パネルを開きます。 Ctrl + 4 キーを押すか、[ネットワーク] アイコンをクリックし、緑色の矢印ボタンをクリックしてネットワーク トラフィックのキャプチャを開始します。

    Initiating Web API network capture

    Web API ネットワーク キャプチャの開始

  6. ブラウザーのアドレス バーの URL に api/trivia を追加します。 次に、TriviaControllerGet アクション メソッドからの応答の詳細を調べます。

    Retrieving the next question data through Web API

    Web API を介して次の質問データを取得

    Note

    ダウンロードが完了すると、ダウンロードしたファイルを使用してアクションを実行するように求められます。 [開発者ツール] ウィンドウで応答コンテンツを監視できるようにするには、ダイアログ ボックスを開いたままにします。

  7. 次に、応答の中身を調べます。 これを行うには、[詳細] タブをクリックし、[応答本文] をクリックします。 ダウンロードしたデータが、TriviaQuestion クラスに対応するプロパティのオプション (TriviaOption オブジェクトの一覧)、IDタイトルを持つオブジェクトであることを確認できます。

    Viewing the Web API Response Body

    Web API 応答本文の表示

  8. Visual Studio に戻り、Shift + F5 キーを押してデバッグを停止します。

演習 2: SPA インターフェイスを作成する

この演習では、AngularJS を使用したシングル ページ アプリケーションの相互作用に焦点を当てて、Geek Quiz の Web フロントエンド部分を最初にビルドします。 その後、CSS3 のユーザー エクスペリエンスを強化して、豊かなアニメーションを実行し、ある質問から次の質問に切り替えるときのコンテキスト切り替えの視覚効果を実現します。

タスク 1 - AngularJS を使用して SPA インターフェイスを作成する

このタスクでは、AngularJS を使用して、Geek Quiz アプリケーションのクライアント側を実装します。 AngularJS は、Model-View-Controller (MVC) 機能を使用してブラウザー ベースのアプリケーションを拡張し、開発とテストの両方を容易にする、オープンソースの JavaScript フレームワークです。

まず、Visual Studio の パッケージ マネージャー コンソールから AngularJS をインストールします。 次に、AngularJS テンプレート エンジンを使用して、Geek Quiz アプリの動作とクイズの質問と解答をレンダリングするビューを提供するコントローラーを作成します。

Note

AngularJS の詳細については、[http://angularjs.org/] (http://angularjs.org/) を参照してください。

  1. Visual Studio Express 2013 for Web を開き、Source/Ex2-CreatingASPAInterface/Begin フォルダーにある GeekQuiz.sln ソリューションを開きます。 または、前の演習で取得したソリューションを続行することもできます。

  2. [ツール]>[NuGet Package Manager] からパッケージ マネージャー コンソールを開きます。 AngularJS.Core NuGet パッケージをインストールするには、次のコマンドを入力します。

    Install-Package AngularJS.Core
    
  3. ソリューション エクスプローラーで、GeekQuiz プロジェクトの Scripts フォルダーを右クリックし、[追加] - [新しいフォルダー] を選択します。 フォルダーに app という名前を付け、Enter キーを押します。

  4. 先ほど作成した app フォルダーを右クリックし、[追加] - [JavaScript] ファイルを選択します。

    Creating a new JavaScript file

    新しい JavaScript ファイルの作成

  5. [項目の名前を指定] ダイアログ ボックスで、[項目名] テキスト ボックスに「quiz-controller」と入力し、[OK] をクリックします。

    Naming the new JavaScript file

    新しい JavaScript ファイルに名前を付ける

  6. quiz-controller.js ファイルに、AngularJS QuizCtrl コントローラーを宣言して初期化する次のコードを追加します。

    (コード スニペット - AspNetWebApiSpa - Ex2 - AngularQuizController)

    angular.module('QuizApp', [])
        .controller('QuizCtrl', function ($scope, $http) {
            $scope.answered = false;
            $scope.title = "loading question...";
            $scope.options = [];
            $scope.correctAnswer = false;
            $scope.working = false;
    
            $scope.answer = function () {
                return $scope.correctAnswer ? 'correct' : 'incorrect';
            };
        });
    

    Note

    QuizCtrl コントローラーのコンストラクター関数には、$scope という名前の挿入可能なパラメーターが必要です。 プロパティを $scope オブジェクトにアタッチすることで、コンストラクター関数にスコープの初期状態を設定する必要があります。 プロパティにはビュー モデルが含まれており、コントローラーの登録時にテンプレートにアクセスできます。

    QuizCtrl コントローラーは、QuizApp という名前のモジュール内で定義されます。 モジュールは、アプリケーションを個別のコンポーネントに分割できる作業単位です。 モジュールを使用する主な利点は、コードが理解しやすくなり、単体テストが容易になり、再利用性と管理性が向上することです。

  7. ここでは、ビューからトリガーされたイベントに対応するために、スコープにビヘイビアーを追加します。 QuizCtrl コントローラーの末尾に次のコードを追加して、$scope オブジェクトで nextQuestion 関数を定義します。

    (コード スニペット - AspNetWebApiSpa - Ex2 - AngularQuizControllerNextQuestion)

    .controller('QuizCtrl', function ($scope, $http) { 
        ...
    
        $scope.nextQuestion = function () {
            $scope.working = true;
            $scope.answered = false;
            $scope.title = "loading question...";
            $scope.options = [];
    
            $http.get("/api/trivia").success(function (data, status, headers, config) {
                $scope.options = data.options;
                $scope.title = data.title;
                $scope.answered = false;
                $scope.working = false;
            }).error(function (data, status, headers, config) {
                $scope.title = "Oops... something went wrong";
                $scope.working = false;
            });
        };
    };
    

    Note

    この関数は、前の演習で作成した Trivia Web API から次の質問を取得し、質問データを $scope オブジェクトにアタッチします。

  8. QuizCtrl コントローラーの末尾に次のコードを挿入して、$scope オブジェクトに sendAnswer 関数を定義します。

    (コード スニペット - AspNetWebApiSpa - Ex2 - AngularQuizControllerSendAnswer)

    .controller('QuizCtrl', function ($scope, $http) { 
        ...
    
        $scope.sendAnswer = function (option) {
            $scope.working = true;
            $scope.answered = true;
    
            $http.post('/api/trivia', { 'questionId': option.questionId, 'optionId': option.id }).success(function (data, status, headers, config) {
                $scope.correctAnswer = (data === true);
                $scope.working = false;
            }).error(function (data, status, headers, config) {
                $scope.title = "Oops... something went wrong";
                $scope.working = false;
            });
        };
    };
    

    Note

    この関数は、ユーザーが選択した解答を Trivia Web API に送信し、結果 (解答が正しいかどうか) を $scope オブジェクトに格納します。

    上記の nextQuestionsendAnswer 関数では、AngularJS $http オブジェクトを使用して、ブラウザーから XMLHttpRequest JavaScript オブジェクトを介して Web API との通信を抽象化します。 AngularJS では、RESTful API を介してリソースに対して CRUD 操作を実行する、より高いレベルの抽象化を実現する別のサービスがサポートされています。 AngularJS $resource オブジェクトには、$http オブジェクトと対話することなく、高レベルのビヘイビアーを提供するアクション メソッドがあります。 CRUD モデルを必要とするシナリオでは、$resource オブジェクトの使用を検討してください (詳細については、$resource のドキュメントを参照してください)。

  9. 次の手順では、クイズのビューを定義する AngularJS テンプレートを作成します。 これを行うには、Views | Home フォルダー内の Index.cshtml ファイルを開き、その内容を次のコードに置き換えます。

    (コード スニペット - AspNetWebApiSpa - Ex2 - GeekQuizView)

    @{
        ViewBag.Title = "Play";
    }
    
    <div id="bodyContainer" ng-app="QuizApp">
        <section id="content">
            <div class="container" >
                <div class="row">
                    <div class="flip-container text-center col-md-12" ng-controller="QuizCtrl" ng-init="nextQuestion()">
                        <div class="back" ng-class="{flip: answered, correct: correctAnswer, incorrect:!correctAnswer}">
                            <p class="lead">{{answer()}}</p>
                            <p>
                                <button class="btn btn-info btn-lg next option" ng-click="nextQuestion()" ng-disabled="working">Next Question</button>
                            </p>
                        </div>
                        <div class="front" ng-class="{flip: answered}">
                            <p class="lead">{{title}}</p>
                            <div class="row text-center">
                                <button class="btn btn-info btn-lg option" ng-repeat="option in options" ng-click="sendAnswer(option)" ng-disabled="working">{{option.title}}</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </div>
    
    @section scripts {
        @Scripts.Render("~/Scripts/angular.js")
        @Scripts.Render("~/Scripts/app/quiz-controller.js")
    }
    

    Note

    AngularJS テンプレートは、モデルとコントローラーからの情報を使用して静的マークアップをユーザーがブラウザーで見る動的ビューに変換する宣言型仕様です。 テンプレートで使用できる AngularJS の要素と要素属性の例を次に示します。

    • ng-app ディレクティブは、アプリケーションのルート要素を表す DOM 要素を AngularJS に通知します。
    • ng-controller ディレクティブは、ディレクティブが宣言されている時点で DOM にコントローラーをアタッチします。
    • 中かっこ表記 {{ }} は、コントローラーで定義されているスコープ プロパティへのバインドを表します。
    • ng-click ディレクティブは、ユーザーのクリックに応答して、スコープで定義された関数を呼び出すために使用されます。
  10. Content フォルダー内の Site.css ファイルを開き、ファイルの末尾に次の強調表示されたスタイルを追加して、クイズ ビューの外観を指定します。

    (コード スニペット - AspNetWebApiSpa - Ex2 - GeekQuizStyles)

    .validation-summary-valid {
         display: none;
    }
    
    /* Geek Quiz styles */
    .flip-container .back,
    .flip-container .front {
         border: 5px solid #00bcf2;
         padding-bottom: 30px;
         padding-top: 30px;
    }
    
    #content {
        position:relative;
        background:#fff;
        padding:50px 0 0 0;
    }
    
    .option {
         width:140px;
         margin: 5px;
    }
    
    div.correct p {
         color: green;
    }
    
    div.incorrect p {
         color: red;
    }
    
    .btn {
         border-radius: 0;
    }
    
    .flip-container div.front, .flip-container div.back.flip {
        display: block;
    }
    
    .flip-container div.front.flip, .flip-container div.back {
        display: none;
    }
    

タスク 2 - ソリューションを実行する

このタスクでは、AngularJS でビルドした新しいユーザー インターフェイスを使用してソリューションを実行し、クイズの質問にいくつか解答します。

  1. F5 キーを押して、ソリューションを実行します。

  2. 新しいユーザー アカウントを登録します。 これを行うには、演習 1 のタスク 3 で説明されている登録手順に従います。

    Note

    前の演習のソリューションを使用している場合は、前に作成したユーザー アカウントでログインできます。

  3. Home ページが表示され、クイズの最初の質問が表示されます。 いずれかのオプションをクリックして、質問に解答します。 これにより、先ほど定義した sendAnswer 関数がトリガーされ、選択したオプションが Trivia Web API に送信されます。

    Answering a question

    質問への解答

  4. いずれかのボタンをクリックすると、解答が表示されます。 [Next Question] をクリックして、次の質問を表示します。 これにより、コントローラーで定義されている nextQuestion 関数がトリガーされます。

    Requesting the next question

    次の質問を要求

  5. 次の質問が表示されます。 何度も質問に答えます。 すべての質問に答えたら、最初の質問に戻ります。

    Another question

    次の質問

  6. Visual Studio に戻り、Shift + F5 キーを押してデバッグを停止します。

タスク 3 - CSS3 を使用してフリップ アニメーションを作成する

このタスクでは、CSS3 プロパティを使用して、質問に解答した後、次の質問が取得されるときに表示するフリップ効果を追加することで、豊かなアニメーションを実行します。

  1. ソリューション エクスプローラーで、GeekQuiz プロジェクトの Content フォルダーを右クリックし、[追加] - [既存の項目] を選択します。

    Adding an existing item to the Content folder

    既存の項目を Content フォルダーに追加

  2. [既存項目の追加] ダイアログ ボックスで、Source/Assets フォルダーに移動し、Flip.css を選択します。 追加をクリックします。

    Adding the Flip.css file from Assets

    Assets からの Flip.css ファイルの追加

  3. 追加した Flip.css ファイルを開き、その内容を調べます。

  4. flip transformation というコメントを見つけます。 このコメントの下のスタイルは、CSS の perspectiverotateY 変換を使用して、"カード フリップ" 効果を生成しています。

    /* flip transformation */
    .flip-container div.front {
        -moz-transform: perspective(2000px) rotateY(0deg);
        -webkit-transform: perspective(2000px) rotateY(0deg);
        -o-transform: perspective(2000px) rotateY(0deg);
        transform: perspective(2000px) rotateY(0deg);
    }
    
        .flip-container div.front.flip {
            -moz-transform: perspective(2000px) rotateY(179.9deg);
            -webkit-transform: perspective(2000px) rotateY(179.9deg);
            -o-transform: perspective(2000px) rotateY(179.9deg);
            transform: perspective(2000px) rotateY(179.9deg);
        }
    
    .flip-container div.back {
        -moz-transform: perspective(2000px) rotateY(-180deg);
        -webkit-transform: perspective(2000px) rotateY(-180deg);
        -o-transform: perspective(2000px) rotateY(-180deg);
        transform: perspective(2000px) rotateY(-180deg);
    }
    
        .flip-container div.back.flip {
            -moz-transform: perspective(2000px) rotateY(0deg);
            -webkit-transform: perspective(2000px) rotateY(0deg);
            -ms-transform: perspective(2000px) rotateY(0);
            -o-transform: perspective(2000px) rotateY(0);
            transform: perspective(2000px) rotateY(0);
        }
    
  5. hide back of pane during flip というコメントを見つけます。 このコメントの下のスタイルは、backface-visibility という CSS プロパティを hidden に設定することで、対象が裏側を向いたときに裏面を見えないようにします。

    /* hide back of pane during flip */
    .front, .back {
        -moz-backface-visibility: hidden;
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
    }
    
  6. App_Start フォルダー内のBundleConfig.cs ファイルを開き、"~/Content/css" スタイル バンドルのFlip.css ファイルに参照を追加します。

    bundles.Add(new StyleBundle("~/Content/css").Include(
        "~/Content/bootstrap.css",
        "~/Content/site.css",
        "~/Content/Flip.css"));
    
  7. F5 キーを押してソリューションを実行し、資格情報を使用してログインします。

  8. いずれかのオプションをクリックして、質問に解答します。 ビュー間を切り替えるときのフリップ効果に注目してください。

    Answering a question with the flip effect

    フリップ効果を使用して質問に解答する

  9. [Next Question] をクリックして、次の質問を取得します。 フリップ効果が再び表示されます。

    Retrieving the following question with the flip effect

    フリップ効果を使用して次の質問を取得する


まとめ

このハンズオン ラボを完了することで、次の方法を学習しました。

  • ASP.NET スキャフォールディングを使用して ASP.NET Web API コントローラーを作成する
  • Web API Get アクションを実装して次のクイズの質問を取得する
  • クイズの解答を格納する Web API Post アクションを実装する
  • Visual Studio パッケージ マネージャー コンソールから AngularJS をインストールする
  • AngularJS のテンプレートとコントローラーを実装する
  • CSS3 変換を使用してアニメーション効果を実行する