共用方式為


實習實驗室:使用 ASP.NET Web API 和 Angular.js 建置單一頁面應用程式 (SPA)

演講者:Web Camps 團隊

下載 Web Camps 培訓套件

本實作教室向您展示如何使用 ASP.NET Web API 和 Angular.js for ASP.NET 4.x 建立單頁應用程式 (SPA)。

在此實作教室中,您將利用這些技術來實現 Geek Quiz,這是一個基於 SPA 概念的問答網站。 您將先使用 ASP.NET Web API 實作服務層,以公開所需的端點來擷取測驗問題並儲存答案。 然後,您將使用 AngularJS 和 CSS3 轉換效果建立豐富且回應靈敏的 UI。

在傳統的網頁應用程式中,用戶端 (瀏覽器) 透過請求頁面來發起與伺服器的通訊。 然後伺服器處理請求並將頁面的 HTML 傳送到用戶端。 在與頁面的後續互動中,例如使用者瀏覽到連結或提交包含資料的表單時,將向伺服器傳送新的請求,流程將重新開始:伺服器處理請求並根據用戶端請求的新動作向瀏覽器傳送新頁面。

在單頁應用程式 (SPA) 中,整個頁面在初始請求後載入到瀏覽器中,但後續互動透過 Ajax 請求進行。 這代表瀏覽器只需更新頁面已變更的部分;無需重新載入整個頁面。 SPA 方法減少了應用程式回應使用者動作所需的時間,從而帶來更流暢的體驗。

SPA 的架構涉及傳統 Web 應用程式中不存在的某些挑戰。 然而,ASP.NET Web API 等新興技術、AngularJS 等 JavaScript 架構以及 CSS3 提供的新樣式功能使設計和建立 SPA 變得非常容易。

所有範例程式碼和片段都包含在 Web Camps 培訓套件中,您可以在 https://aka.ms/webcamps-training-kit 取得這些內容。

概觀

目標

在這個實作教室中,您將學習如何:

  • 建立 ASP.NET Web API 服務來傳送和接收 JSON 資料
  • 使用 AngularJS 建立回應式 UI
  • 透過 CSS3 轉換增強 UI 體驗

必要條件

完成此實作教室需要滿足以下條件:

設定

為了運行這個實作教室中的練習,您需要先設定環境。

  1. 開啟 Windows Explorer ,然後瀏覽到實作教室的 Source 資料夾。
  2. 以滑鼠右鍵按一下 Setup.cmd,然後選擇「以管理員身份執行」以啟動安裝程序,該程序將設定您的環境並安裝本實驗的 Visual Studio 程式碼片段。
  3. 如果顯示「使用者帳戶控制」對話方塊,請確認動作以繼續。

注意

在執行安裝程式之前,請確保您已檢查此實驗的所有依賴項。

使用程式碼片段

在整個實作教室文件中,系統將指示您插入程式碼區塊。 為了方便起見,這些程式碼大多作為 Visual Studio 程式碼片段提供,您可以在 Visual Studio 2013 中存取它們,以避免手動新增。

注意

每個練習都附有位於練習的 Begin 資料夾中的起始解決方案,使您可以獨立於其他練習進行每個練習。 請注意,這些起始解決方案中缺少練習期間新增的程式碼片段,並且在您完成練習之前可能無法使用。 在練習的原始程式碼中,您還會找到一個 End 資料夾,其中包含 Visual Studio 解決方案以及完成對應練習中的步驟所產生的程式碼。 如果在進行這個實作教室時需要額外幫助,您可以使用這些解決方案作為指導。


練習

這個實作教室包括以下練習:

  1. 建立 Web API
  2. 建立 SPA 介面

完成這個實作教室的預估時間:60 分鐘

注意

第一次啟動 Visual Studio 時,必須選擇其中一個預先定義的設定集合。 每個預定義集合旨在匹配特定的開發風格,並決定視窗版面設定、編輯器行為、IntelliSense 程式碼片段和對話方塊選項。 本實驗中的過程描述了使用一般開發設定集合時在 Visual Studio 中完成給定任務所需的動作。 如果您為開發環境選擇不同的設定集合,則應考慮的步驟可能會有所不同。

練習 1:建立 Web API

SPA 的關鍵部分之一是服務層。 它負責處理 UI 傳送的 Ajax 呼叫並傳回資料以回應該呼叫。 檢索到的資料應以機器可讀的格式呈現,以便用戶端解析和使用。

Web API 架構是 ASP.NET Stack 的一部分,旨在輕鬆實作 HTTP 服務,通常透過 RESTful API 傳送和接收 JSON 或 XML 格式的資料。 在本練習中,您將建立網站來裝載極客測驗應用程式,然後實作後端服務以使用 ASP.NET Web API 公開和保留測驗資料。

任務 1 – 建立極客測驗的初始項目

在此任務中,您將開始建立一個新的 ASP.NET MVC 項目,支援基於 Visual Studio 隨附的 One ASP.NET 專案類型的 ASP.NET Web API。 One ASP.NET 即統一所有 ASP.NET 技術,並讓您可以根據需要自由混合和搭配這些技術。 然後,您將新增 Entity Framework 的模型類別和資料庫初始設定式,以插入測驗問題。

  1. 打開 Web 版 Visual Studio Express 2013,然後選擇「檔案 | 新專案...」以開始一個新解決方案。

    建立新專案

    建立新專案

  2. 在「新專案」對話方塊中,選擇「Visual C# | Web」標籤下的「ASP.NET Web 應用程式」。確保選擇 .NET Framework 4.5,將其命名為 GeekQuiz,選擇「位置」並按一下「確定」。

    建立新的 ASP.NET Web 應用程式專案

    建立新的 ASP.NET Web 應用程式專案

  3. 在「新的 ASP.NET 專案」對話方塊中,選擇 MVC 範本並選擇「Web API」選項。 同時,確保驗證選項設定為「個人使用者帳戶」。 按一下 [確定] 繼續進行。

    使用 MVC 範本建立新項目,包括 Web API 元件

    使用 MVC 範本建立新項目,包括 Web API 元件

  4. 在「方案總管」中,以滑鼠右鍵按一下 GeekQuiz 專案的Models 資料夾,然後選擇「新增 | 現有項目...」。

    新增現有項目

    新增現有項目

  5. 在「新增現有項目」對話方塊中,導覽至 Source/Assets/Models 資料夾,然後選擇所有檔案。 按一下新增

    新增模型資源

    新增模型資源

    注意

    透過新增這些檔案,您將為 Geek Quiz 應用程式新增資料模型、Entity Framework 的資料庫上下文,和資料庫初始設定式。

    Entity Framework (EF) 是一種物件關聯對應工具 (ORM),可讓您透過使用概念應用程式模型進行程式設計來建立資料存取應用程式,而不是直接使用關聯式儲存結構描述進行程式設計。 您可以在這裡了解有關 Entity Framework 的更多資訊。

    以下是您剛剛新增類別的描述:

    • TriviaOption:代表與測驗問題相關的單一選項
    • TriviaQuestion:代表一個測驗問題,並透過 Options 屬性公開關聯的選項
    • TriviaAnswer:代表使用者在回答測驗問題時選擇的選項
    • TriviaContext:表示 Geek Quiz 應用程式的 Entity Framework 的資料庫上下文。 此類衍生自 DContext,並公開表示上述實體集合的 DbSet 屬性。
    • TriviaDatabaseInitializer:繼承自 CreateDatabaseIfNotExistsTriviaContext 類別的 Entity Framework 初始設定式的實作。 此類的預設行為是僅在資料庫不存在時建立資料庫,並插入在 Seed 方法中指定的實體。
  6. 開啟 Global.asax.cs 檔案並新增以下 using 陳述式。

    using GeekQuiz.Models;
    
  7. Application_Start 方法的開頭新增下列程式碼,以將 TriviaDatabaseInitializer 設定為資料庫初始設定式。

    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();
            }
    
            ...
        }
    }
    

    注意

    授權篩選器檢查使用者是否經過驗證。 如果使用者未經過驗證,它將傳回 HTTP 狀態代碼 401 (未經授權),而不呼叫該動作。 您可以在全域、控制器層級或單一動作層級套用篩選器。

  9. 您現在將自訂網頁的版面配置和品牌。 若要這樣做,打開 Views | Shared 資料夾中的 _Layout.cshtml 檔案,並透過將「My ASP.NET Application」替換為「Geek 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. 在同一檔案中,透過移除「關於」和「聯絡人」連結,並將「首頁」連結重新命名為「播放」來更新導覽列。 此外,將應用程式名稱連結重新命名為 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. 透過 Geek Quiz 取代 My ASP.NET Application 來更新版面配置頁面的頁尾。 為此,請將 <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 }
            );
        }
    }
    

    注意

    CamelCasePropertyNamesContractResolver 自動將屬性名稱轉換為駝峰式大小寫,這是 JavaScript 中屬性名稱的一般慣例。

  4. 在「方案總管」中,以滑鼠右鍵按一下 GeekQuiz 專案的 Controllers 資料夾,然後選擇「新增 | 新的 Scaffolded 項目....

    建立一個新的 scaffolded 項目

    建立一個新的 scaffolded 項目

  5. 在「新增 Scaffold」對話方塊中,請確保在左側窗格中選擇「通用」節點。 然後,在中心窗格中選擇 Web API 2 控制器 - 空白 範本,然後按一下「新增」。

    選擇「Web API 2 控制器空白」範本

    選擇「Web API 2 控制器空白」範本

    注意

    ASP.NET Scaffolding 是 ASP.NET Web 應用程式的程式碼產生架構。 Visual Studio 2013 包含用於 MVC 和 Web API 專案的預先安裝程式碼產生器。 當您想要快速新增與資料模型互動的程式碼以減少開發標準資料操作所需的時間時,您應該在專案中使用 Scaffolding。

    Scaffolding 過程還確保專案中安裝了所有必需的依賴項。 例如,如果您從空白 ASP.NET 專案開始,接著使用 Scaffolding 新增 Web API 控制器,則必要的 Web API NuGet 套件和參考會自動新增至您的專案。

  6. 在「新增控制器」對話方塊中,在「控制器名稱」文字方塊中鍵入 TriviaController,然後按一下「新增」。

    新增 Trivia 控制器

    新增 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);
        }
    }
    

    注意

    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. 透過將 Authorize 屬性新增至 TriviaController 類別定義,修改 Web API 控制器以限制對經過驗證的使用者的存取。

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

任務 3 – 運行解決方案

在此任務中,您將驗證在上一個任務中建立的 Web API 服務是否如預期運作。 您將使用 Internet Explorer F12 開發人員工具擷取網路流量,並檢查來自 Web API 服務的完整回應。

注意

請確認您在 Visual Studio 工具列上的「開始」按鈕中選擇了 Internet Explorer

Internet Explorer 選項

  1. F5 運行解決方案。 瀏覽器應該會出現登入頁面。

    注意

    當應用程式啟動時,會觸發預設的 MVC 路由,該路由預設會對應到 HomeController 類別的 Index 動作。 由於 HomeController 僅限於經過驗證的使用者 (請記住,您在練習 1 中使用 Authorize 屬性修飾了該類別) 並且尚未對使用者進行驗證,因此應用程式將原始請求重新導向到登入頁面。

    執行解決方案

    執行解決方案

  2. 按一下註冊以建立新使用者。

    註冊新使用者

    註冊新使用者

  3. 註冊頁面中,輸入使用者名稱密碼,然後按一下「註冊」。

    註冊頁面

    註冊頁面

  4. 應用程式註冊新帳戶,使用者經過驗證並重新導向回首頁。

    使用者受

    使用者受

  5. 在瀏覽器中,按 F12 開啟「開發人員工具」面板。 按 CTRL + 4 或按一下「網路」圖示,然後按一下綠色箭頭按鈕開始擷取網路流量。

    啟動 Web API 網路擷取

    啟動 Web API 網路擷取

  6. api/trivia 附加到瀏覽器網址列中的 URL。 現在,您將檢查 TriviaControllerGet 動作方法的回應詳細資訊。

    透過 Web API 檢索下一個問題資料

    透過 Web API 檢索下一個問題資料

    注意

    下載完成後,系統會提示您對下載的檔案進行動作。 保持對話方塊打開,以便能夠透過「開發人員工具」視窗觀看回應內容。

  7. 現在您將檢查回應的本文。 為此,請按一下「詳細資料」索引標籤,然後按一下「回應本文」。 您可以檢查下載的資料是否為一個物件,其屬性 options (這是 TriviaOption 物件的清單)、idtitleTriviaQuestion 類別相對應。

    查看 Web API 回應本文

    查看 Web API 回應本文

  8. 返回 Visual Studio 並按 SHIFT + F5 停止偵錯。

練習 2:建立 SPA 介面

在本練習中,您將先建立 Geek Quiz 的 Web 前端部分,重點關注使用 AngularJS 的單頁應用程式互動。 然後,您將使用 CSS3 增強使用者體驗,以執行豐富的動畫,並在從一個問題過渡到下一個問題時提供上下文切換的視覺效果。

任務 1 – 使用 AngularJS 建立 SPA 介面

在此任務中,您將使用 AngularJS 實作 Geek Quiz 應用程式的用戶端。 AngularJS 是一個開源 JavaScript 架構,它透過模型-檢視-控制器 (MVC) 功能增強基於瀏覽器的應用程式,從而促進開發和測試。

您將先從 Visual Studio 的套件管理員主控台安裝 AngularJS。 然後,您將建立控制器以提供 Geek Quiz 應用程式的行為,並建立檢視以使用 AngularJS 範本引擎呈現測驗問題和答案。

注意

有關 AngularJS 的更多資訊,請參閱 [http://angularjs.org/](http://angularjs.org/)。

  1. 打開 Web 版 Visual Studio Express 2013,然後打開位於 Source/Ex2-CreatingASPAInterface/Begin 資料夾中的 GeekQuiz.sln 解決方案。 或者,您可以繼續使用上一個練習中獲得的解決方案。

  2. 工具>NuGet 套件管理員開啟套件管理員主控台。 鍵入以下命令來安裝 AngularJS.Core NuGet 套件。

    Install-Package AngularJS.Core
    
  3. 在「方案總管」中,以滑鼠右鍵按一下 GeekQuiz 專案的 Scripts 資料夾,然後選擇「新增 | 新資料夾」。 將資料夾命名為 app,然後按 Enter 鍵。

  4. 以滑鼠右鍵按一下您剛剛建立的 app 資料夾,然後選擇「新增 | JavaScript 檔案」。

    建立一個新的 JavaScript 檔案

    建立一個新的 JavaScript 檔案

  5. 在「指定項目名稱」對話方塊中,在「項目名稱」文字方塊中鍵入 quiz-controller,然後按一下「確定」。

    命名新的 JavaScript 檔案

    命名新的 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';
            };
        });
    

    注意

    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;
            });
        };
    };
    

    注意

    此函式從上一練習中建立的 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;
            });
        };
    };
    

    注意

    這個函式會將使用者選擇的答案傳送到 Trivia Web API,並將結果 (即答案是否正確) 儲存在 $scope 物件中。

    上述的 nextQuestionsendAnswer 函式使用 AngularJS 的 $http 物件來抽象化與 Web API 的通訊,透過瀏覽器中的 XMLHttpRequest JavaScript 物件進行互動。 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")
    }
    

    注意

    AngularJS 範本是一種宣告式規範,它使用來自模型和控制器的資訊將靜態標記轉換為使用者在瀏覽器中看到的動態檢視。 以下是可在範本中使用的 AngularJS 元素和元素屬性的範例:

    • ng-app 指示詞會告訴 AngularJS 代表應用程式根元素的 DOM 元素。
    • 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 所述的註冊步驟進行操作。

    注意

    如果您使用上一個練習中的解決方案,則可以使用先前建立的使用者帳戶登入。

  3. 這應該會出現首頁,顯示測驗的第一個問題。 透過按一下選項之一回答問題。 這將觸發先前定義的 sendAnswer 函式,該函式會將所選選項傳送至 Trivia Web API。

    回答問題

    回答問題

  4. 按一下其中一個按鈕後,應該會出現答案。 按一下「下一個問題」以顯示以下問題。 這將觸發控制器中定義的 nextQuestion 函式。

    請求下一個問題

    請求下一個問題

  5. 應該會出現下一個問題。 繼續根據需要多次回答問題。 完成所有問題後,您應該會回到第一個問題。

    另一個問題

    下一個問題

  6. 返回 Visual Studio 並按 SHIFT + F5 停止偵錯。

任務 3 – 使用 CSS3 建立翻轉動畫

在此任務中,您將使用 CSS3 屬性來執行豐富的動畫,方法是在回答問題和檢索下一個問題時新增翻轉效果。

  1. 在「方案總管」中,以滑鼠右鍵按一下 GeekQuiz 專案的Content 資料夾,然後選擇「新增 | 現有項目...」。

    將現有項目新增至 Content 資料夾

    將現有項目新增至 Content 資料夾

  2. 在「新增現有項目」對話方塊中,導覽至「Source/Assets」資料夾並選擇「Flip.css」。 按一下新增

    從資產中新增 Flip.css css檔案

    從資產中新增 Flip.css css檔案

  3. 打開剛剛新增的 Flip.css 檔案並檢查其內容。

  4. 找到翻轉變換註釋。 該註釋下方的樣式使用 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. 找到「翻轉時隱藏背面的面板」的註釋。 該註釋下方的樣式透過將 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. 透過按一下其中一個選項來回答問題。 請注意在檢視之間轉換時的翻轉效果。

    使用翻轉效果回答問題

    使用翻轉效果回答問題

  9. 按一下「下一個問題」以檢索以下問題。 翻轉效果應該會再次出現。

    使用翻轉效果檢索以下問題

    使用翻轉效果檢索以下問題


摘要

透過完成這個實作教室,您已經了解如何:

  • 使用 ASP.NET Scaffolding 建立 ASP.NET Web API 控制器
  • 實作 Web API Get 動作來檢索下一個測驗問題
  • 實作 Web API Post 動作來儲存測驗答案
  • 從 Visual Studio 套件管理員主控台安裝 AngularJS
  • 實作 AngularJS 範本和控制器
  • 使用 CSS3 轉換來執行動畫效果