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

Web 擷取小組

下載 Web Training Kit

此實際操作實驗室會示範如何使用 ASP.NET Web API 和 Angular.js 來建置單頁應用程式 (SPA) ,以進行 ASP.NET 4.x。

在此實作實驗室中,您將利用這些技術來實作 Geek Quiz,這是以 SPA 概念為基礎的 Trivia 網站。 您將先使用 ASP.NET Web API 來實作服務層,以公開必要的端點來擷取測驗問題並儲存答案。 然後,您將使用 AngularJS 和 CSS3 轉換效果來建置豐富的回應式 UI。

在傳統的 Web 應用程式中,用戶端 (瀏覽器) 藉由要求頁面來起始與伺服器的通訊。 然後,伺服器會處理要求,並將頁面的 HTML 傳送給用戶端。 在與頁面的後續互動中,例如使用者流覽至連結或提交含有資料的表單– 新的要求會傳送至伺服器,而流程會再次啟動:伺服器會處理要求,並將新頁面傳送至瀏覽器,以回應用戶端所要求的新動作。

在 Single-Page 應用程式 (SPA) 整個頁面會在初始要求之後載入瀏覽器中,但後續的互動會透過 Ajax 要求進行。 這表示瀏覽器必須只更新已變更的頁面部分;不需要重載整個頁面。 SPA 方法可減少應用程式回應使用者動作所花費的時間,進而產生更流暢的體驗。

SPA 的架構牽涉到傳統 Web 應用程式中不存在的特定挑戰。 不過,ASP.NET Web API、AngularJS 等 JavaScript 架構和 CSS3 所提供的新樣式功能等新興技術,可讓您輕鬆地設計和建置 SPA。

所有範例代碼和程式碼片段都包含在 Web Training Kit 中,可在 https://aka.ms/webcamps-training-kit 取得。

概觀

目標

在此實際操作實驗室中,您將瞭解如何:

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

必要條件

需要下列專案才能完成此實際操作實驗室:

安裝程式

若要在此實際操作實驗室中執行練習,您必須先設定環境。

  1. 開啟 Windows 檔案總管並流覽至實驗室的 [來源] 資料夾。
  2. 以滑鼠右鍵按一下 Setup.cmd ,然後選取 [ 以系統管理員身分執行 ] 以啟動安裝程式,以設定您的環境,並安裝此實驗室的 Visual Studio 程式碼片段。
  3. 如果顯示 [使用者帳戶控制] 對話方塊,請確認要繼續的動作。

注意

在執行安裝程式之前,請確定您已檢查此實驗室的所有相依性。

使用程式碼片段

在整個實驗室檔中,系統會指示您插入程式碼區塊。 為了方便起見,大部分的程式碼都會以Visual Studio Code程式碼片段的形式提供,您可以從Visual Studio 2013記憶體取,以避免手動新增。

注意

每個練習都會伴隨位於練習的 Begin 資料夾中的起始解決方案,可讓您獨立于其他練習個別追蹤每個練習。 請注意,在練習期間新增的程式碼片段會從這些起始解決方案中遺失,而且在您完成練習之前可能無法運作。 在練習的原始程式碼內,您也會找到包含 Visual Studio 解決方案的 End 資料夾,其中包含完成對應練習中步驟所產生的程式碼。 如果您需要額外的協助,請透過此實際操作實驗室,使用這些解決方案作為指引。


Exercises

此實際操作實驗室包含下列練習:

  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 格式的資料。 在此練習中,您將建立網站來裝載 Geek 測驗應用程式,然後實作後端服務,以使用 ASP.NET Web API公開及保存測驗資料。

工作 1 – 建立 Geek 測驗的初始專案

在這項工作中,您將開始建立新的 ASP.NET MVC 專案,並根據 Visual Studio 隨附的One ASP.NET專案類型,支援 ASP.NET Web API。 其中一個 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. 在 [ 新增現有專案 ] 對話方塊中,流覽至 [來源/資產/模型 ] 資料夾,然後選取所有檔案。 按一下 [新增] 。

    新增模型資產 新增

    新增模型資產

    注意

    藉由新增這些檔案,您會新增資料模型、Entity Framework 的資料庫內容,以及 Geek 測驗應用程式的資料庫初始化運算式。

    Entity Framework (EF) 是物件關聯式對應程式 (ORM) ,可讓您使用概念應用程式模型進行程式設計,而不是直接使用關聯式儲存體架構進行程式設計,以建立資料存取應用程式。 您可以 在這裡深入瞭解 Entity Framework。

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

    • TriviaOption: 代表與測驗問題相關聯的單一選項
    • TriviaQuestion: 代表測驗問題,並透過 Options 屬性公開相關聯的選項
    • TriviaAnswer: 代表使用者選取的選項,以回應測驗問題
    • TriviaCoNtext: 代表 Geek 測驗應用程式的 Entity Framework 資料庫內容。 這個類別衍生自 DCoNtext ,並公開代表上述實體集合的 DbSet 屬性。
    • TriviaDatabaseInitializer:CreateDatabaseIfNotExists繼承之TriviaCoNtext類別的 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 內開啟_Layout.cshtml檔案|共用資料夾,並以Geek 測驗取代[我的 ASP.NET 應用程式],以更新標題 > 元素的內容 <

    <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 測驗。 導覽列的 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 測驗取代[我的 ASP.NET 應用程式],以更新版面配置頁面的頁尾。 若要這樣做,請將頁 > 尾專案的內容 <取代為下列反白顯示的程式碼。

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

工作 2 – 建立 TriviaController Web API

在上一個工作中,您已建立 Geek 測驗 Web 應用程式的初始結構。 您現在會建置與測驗資料模型互動的簡單 Web API 服務,並公開下列動作:

  • GET /api/trivia:從已驗證的使用者要回答的測驗清單中擷取下一個問題。
  • POST /api/trivia:儲存已驗證使用者指定的測驗答案。

您將使用 Visual Studio 所提供的 ASP.NET Scaffolding 工具來建立 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會自動將屬性名稱轉換成 camel 大小,這是 JavaScript 中屬性名稱的一般慣例。

  4. 方案總管中,以滑鼠右鍵按一下GeekQuiz專案的Controllers資料夾,然後選取 [新增] |新增 Scaffolded Item...

    建立新的 Scaffold 專案

    建立新的 Scaffold 專案

  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 協助程式方法。 然後,它會傳送回應,其中包含 Helper 方法所傳回的布林值。

    (程式碼片段 - 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. 修改 Web API 控制器,藉由將 Authorize 屬性新增至 TriviaController 類別定義,以限制對已驗證使用者的存取。

    [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 擷取下一個問題資料

    注意

    下載完成後,系統會提示您使用下載的檔案執行動作。 讓對話方塊保持開啟,以便能夠透過 [開發人員工具] 視窗watch回應內容。

  7. 現在您將檢查回應的本文。 若要這樣做,請按一下 [ 詳細資料] 索引標籤,然後按一下 [回應本文]。 您可以檢查下載的資料是否為具有屬性選項的物件, (這是與TriviaQuestion 類別對應的 TriviaOption物件清單) 、識別碼標題

    檢視 Web API 回應本文

    檢視 Web API 回應本文

  8. 返回至 Visual Studio,然後按SHIFT + F5停止偵錯。

練習 2:建立 SPA 介面

在此練習中,您將先建置 Geek 測驗的 Web 前端部分,著重于使用 AngularJS的Single-Page應用程式互動。 然後,您將增強 CSS3 的使用者體驗,以執行豐富的動畫,並在從一個問題轉換至下一個問題時提供內容切換的視覺效果。

工作 1 – 使用 AngularJS 建立 SPA 介面

在此工作中,您將使用 AngularJS 來實作 Geek 測驗應用程式的用戶端。 AngularJS 是開放原始碼 JavaScript 架構,可透過 Model-View-Controller (MVC) 功能來增強瀏覽器型應用程式,以加速開發和測試。

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

注意

如需 AngularJS 的詳細資訊,請參閱 [ http://angularjs.org/ ] (http://angularjs.org/) 。

  1. 開啟Visual Studio Express 2013 for Web,然後開啟位於 Source/Ex2-CreateASPAInterface/Begin資料夾中的GeekQuiz.sln解決方案。 或者,您可以繼續進行您在上一個練習中取得的解決方案。

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

    Install-Package AngularJS.Core
    
  3. 方案總管中,以滑鼠右鍵按一下GeekQuiz專案的[腳本] 資料夾,然後選取 [新增] |新增資料夾。 將資料夾命名為 應用程式 ,然後按 Enter鍵。

  4. 以滑鼠右鍵按一下您剛才建立 的應用程式 資料夾,然後選取 [ 新增] |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物件,從瀏覽器透過 XMLHttpRequest JavaScript 物件來抽象化與 Web API 的通訊。 AngularJS 支援另一項服務,此服務提供較高層級的抽象概念,以透過 RESTful API 對資源執行 CRUD 作業。 AngularJS $resource 物件具有動作方法,可提供高階行為,而不需要與 $HTTP 物件互動。 請考慮在需要 CRUD 模型 (fore 資訊的案例中使用 $resource 物件,請參閱 $resource檔)

  9. 下一個步驟是建立 AngularJS 範本,以定義測驗的檢視。 若要這樣做,請在 Views 內開啟 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 資料夾

  2. 在 [ 新增現有專案 ] 對話方塊中,流覽至 [來源/資產] 資料夾,然後選取 [Flip.css]。 按一下 [新增] 。

    從資產新增 Flip.css 檔案從

    從 Assets 新增 Flip.css 檔案

  3. 開啟您剛才新增的 Flip.css 檔案,並檢查其內容。

  4. 找出 翻轉轉換 批註。 以下批註的樣式會使用 CSS 檢視方塊旋轉轉換 來產生「卡片翻轉」效果。

    /* 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. 翻轉批註期間找出窗格的隱藏背面 。 下列樣式,將 反面可見度 CSS 屬性設定為 隱藏,以隱藏臉部的背面。

    /* 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 取得動作以擷取下一個測驗問題
  • 實作 Web API 張貼動作來儲存測驗答案
  • 從 Visual Studio 套件管理員主控台安裝 AngularJS
  • 實作 AngularJS 範本和控制器
  • 使用 CSS3 轉換來執行動畫效果