Laboratorium praktyczne: tworzenie aplikacji jednostronicowej (SPA) przy użyciu internetowego interfejsu API ASP.NET i Angular.js

Autor: Web Camps Team

Pobierz zestaw szkoleniowy obozów internetowych

To praktyczne laboratorium pokazuje, jak utworzyć aplikację jednostronicową (SPA) przy użyciu interfejsu API sieci Web ASP.NET i Angular.js dla ASP.NET 4.x.

W tym warsztacie skorzystasz z tych technologii, aby zaimplementować Geek Quiz, stronę internetową z quizami opartą na koncepcji SPA. Najpierw zaimplementujesz warstwę serwisową przy użyciu ASP.NET Web API, aby udostępnić wymagane interfejsy końcowe do pobierania pytań testowych i zapisywania odpowiedzi. Następnie utworzysz rozbudowany i dynamiczny interfejs użytkownika przy użyciu efektów transformacji AngularJS i CSS3.

W tradycyjnych aplikacjach internetowych klient (przeglądarka) inicjuje komunikację z serwerem, żądając strony. Następnie serwer przetwarza żądanie i wysyła kod HTML strony do klienta. W kolejnych interakcjach ze stroną — np. użytkownik przechodzi do linku lub przesyła formularz z danymi — nowe żądanie jest wysyłane do serwera, a przepływ jest uruchamiany ponownie: serwer przetwarza żądanie i wysyła nową stronę do przeglądarki w odpowiedzi na nową akcję żądaną przez klienta.

W aplikacjach Single-Page (SPA) cała strona jest ładowana w przeglądarce po początkowym żądaniu, ale kolejne interakcje odbywają się za pośrednictwem żądań Ajax. Oznacza to, że przeglądarka musi zaktualizować tylko część strony, która uległa zmianie; Nie ma potrzeby ponownego ładowania całej strony. Podejście SPA skraca czas potrzebny aplikacji na reagowanie na działania użytkownika, co skutkuje bardziej płynnym doświadczeniem.

Architektura SPA obejmuje pewne wyzwania, które nie są obecne w tradycyjnych aplikacjach internetowych. Jednak nowe technologie, takie jak ASP.NET internetowy interfejs API, struktury Języka JavaScript, takie jak AngularJS i nowe funkcje stylów udostępniane przez css3, sprawiają, że naprawdę łatwo projektować i tworzyć elementy SPA.

Cały przykładowy kod i fragmenty kodu znajdują się w zestawie szkoleniowym Web Camps dostępnym pod adresem https://aka.ms/webcamps-training-kit.

Przegląd

Cele i zadania

W tym praktycznym laboratorium dowiesz się, jak wykonywać następujące działania:

  • Tworzenie usługi internetowego interfejsu API ASP.NET do wysyłania i odbierania danych JSON
  • Tworzenie dynamicznego interfejsu użytkownika przy użyciu platformy AngularJS
  • Ulepszanie środowiska interfejsu użytkownika za pomocą przekształceń CSS3

Wymagania wstępne

Do ukończenia tego laboratorium praktycznego wymagane jest wykonanie następujących czynności:

Konfiguracja

Aby uruchomić ćwiczenia w tym praktycznym laboratorium, należy najpierw skonfigurować środowisko.

  1. Otwórz Eksploratora Windows i przejdź do folderu Source laboratorium.
  2. Kliknij prawym przyciskiem myszy Setup.cmd i wybierz polecenie Uruchom jako administrator , aby uruchomić proces instalacji, który skonfiguruje środowisko i zainstaluje fragmenty kodu programu Visual Studio dla tego laboratorium.
  3. Jeśli zostanie wyświetlone okno dialogowe Kontrola konta użytkownika, potwierdź akcję, aby kontynuować.

Uwaga / Notatka

Przed uruchomieniem konfiguracji upewnij się, że zostały sprawdzone wszystkie zależności dla tego laboratorium.

Używanie fragmentów kodu

W całym dokumencie laboratoryjnym będziesz otrzymywać instrukcje dotyczące wstawiania bloków kodu. Dla wygody większość tego kodu jest dostarczana jako fragmenty kodu programu Visual Studio Code, do których można uzyskać dostęp z poziomu programu Visual Studio 2013, aby uniknąć konieczności ręcznego dodawania go.

Uwaga / Notatka

Każdemu ćwiczeniu towarzyszy rozwiązanie początkowe znajdujące się w folderze Begin ćwiczenia, które umożliwia wykonywanie poszczególnych ćwiczeń niezależnie od innych. Pamiętaj, że w tych rozwiązaniach początkowych brakuje fragmentów kodu dodanych podczas ćwiczenia i mogą nie działać do momentu ukończenia ćwiczenia. Wewnątrz kodu źródłowego ćwiczenia znajdziesz również folder Końcowy zawierający rozwiązanie programu Visual Studio z kodem, który wynika z wykonania kroków w odpowiednim ćwiczeniu. Możesz użyć tych rozwiązań jako wskazówek, jeśli potrzebujesz dodatkowej pomocy podczas pracy z tym praktycznym laboratorium.


Ćwiczenia

To praktyczne laboratorium obejmuje następujące ćwiczenia:

  1. Tworzenie internetowego interfejsu API
  2. Tworzenie interfejsu SPA

Szacowany czas ukończenia tego laboratorium: 60 minut

Uwaga / Notatka

Po pierwszym uruchomieniu programu Visual Studio należy wybrać jedną ze wstępnie zdefiniowanych kolekcji ustawień. Każda wstępnie zdefiniowana kolekcja jest przeznaczona do dopasowania określonego stylu programowania i określa układy okien, zachowanie edytora, fragmenty kodu intelliSense i opcje okna dialogowego. Procedury w tym laboratorium opisują akcje niezbędne do wykonania danego zadania w programie Visual Studio podczas korzystania z kolekcji Ogólne ustawienia programowania . W przypadku wybrania innej kolekcji ustawień dla środowiska projektowego mogą wystąpić różnice w krokach, które należy wziąć pod uwagę.

Ćwiczenie 1. Tworzenie internetowego interfejsu API

Jedną z kluczowych części SPA jest warstwa usługi. Odpowiada za przetwarzanie wywołań Ajax wysyłanych przez interfejs użytkownika i zwracanie danych w odpowiedzi na to wywołanie. Pobrane dane powinny być prezentowane w formacie czytelnym dla maszyny, aby można je było analizować i wykorzystywać przez klienta.

Struktura internetowego interfejsu API jest częścią ASP.NET Stack i została zaprojektowana tak, aby ułatwić implementowanie usług HTTP, zazwyczaj wysyłając i odbierając dane w formacie JSON lub XML za pośrednictwem interfejsu API RESTful. W tym ćwiczeniu utworzysz witrynę internetową do hostowania aplikacji Geek Quiz, a następnie zaimplementujesz usługę zaplecza w celu uwidocznienia i utrwalania danych testowych przy użyciu interfejsu API internetowego ASP.NET.

Zadanie 1 — Tworzenie projektu startowego dla Geek Quiz

W tym zadaniu rozpoczniesz tworzenie nowego projektu ASP.NET MVC z obsługą ASP.NET Web API opartego na typie projektu One ASP.NET, który jest dostarczany z Visual Studio. Jedna ASP.NET jednoczy wszystkie technologie ASP.NET i daje możliwość połączenia i dopasowania ich zgodnie z potrzebami. Następnie dodasz klasy modelu programu Entity Framework i inicjator bazy danych, aby wstawić pytania testowe.

  1. Otwórz program Visual Studio Express 2013 dla sieci Web i wybierz pozycję Plik | Nowy projekt... aby rozpocząć nowe rozwiązanie.

    Tworzenie nowego projektu

    Tworzenie nowego projektu

  2. W oknie dialogowym Nowy projekt wybierz pozycję ASP.NET aplikacja internetowa w obszarze Visual C# | Karta sieci Web . Upewnij się, że wybrano program .NET Framework 4.5 , nadaj mu nazwę GeekQuiz, wybierz lokalizację i kliknij przycisk OK.

    Tworzenie nowego projektu aplikacji internetowej ASP.NET

    Tworzenie nowego projektu aplikacji internetowej ASP.NET

  3. W oknie dialogowym Nowy projekt ASP.NET wybierz szablon MVC i wybierz opcję Internetowy interfejs API . Upewnij się również, że opcja Uwierzytelnianie jest ustawiona na indywidualne konta użytkowników. Kliknij przycisk OK, aby kontynuować.

    Tworzenie nowego projektu przy użyciu szablonu MVC, w tym składników internetowego interfejsu API

    Tworzenie nowego projektu przy użyciu szablonu MVC, w tym składników internetowego interfejsu API

  4. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy folder Models projektu GeekQuiz i wybierz pozycję Dodaj | Istniejący element....

    Dodawanie istniejącego elementu Dodawanie

    Dodawanie istniejącego elementu

  5. W oknie dialogowym Dodawanie istniejącego elementu przejdź do folderu Source/Assets/Models i wybierz wszystkie pliki. Kliknij przycisk Dodaj.

    Dodawanie zasobów modelu

    Dodawanie zasobów modelu

    Uwaga / Notatka

    Dodając te pliki, dodajesz model danych, kontekst bazy danych programu Entity Framework i inicjator bazy danych dla aplikacji Geek Quiz.

    Entity Framework (EF) to maper obiektowo-relacyjny (ORM), który umożliwia tworzenie aplikacji dostępu do danych przez programowanie za pomocą koncepcyjnego modelu aplikacji zamiast programowania bezpośrednio przy użyciu schematu magazynu relacyjnego. Więcej informacji na temat programu Entity Framework można znaleźć tutaj.

    Poniżej przedstawiono opis właśnie dodanych klas:

    • TriviaOption: reprezentuje jedną opcję skojarzona z pytaniem testowym
    • TriviaQuestion: reprezentuje pytanie quizu i uwidacznia skojarzone opcje za pomocą właściwości Opcje
    • TriviaAnswer: reprezentuje opcję wybraną przez użytkownika w odpowiedzi na pytanie quizu
    • TriviaContext: reprezentuje kontekst bazy danych programu Entity Framework aplikacji Geek Quiz. Ta klasa pochodzi z obiektu DContext i uwidacznia właściwości DbSet , które reprezentują kolekcje jednostek opisanych powyżej.
    • TriviaDatabaseInitializer: implementacja inicjatora programu Entity Framework dla klasy TriviaContext , która dziedziczy z klasy CreateDatabaseIfNotExists. Domyślne zachowanie tej klasy polega na utworzeniu bazy danych tylko wtedy, gdy nie istnieje, wstawiając jednostki określone w metodzie Seed .
  6. Otwórz plik Global.asax.cs i dodaj następującą instrukcję using.

    using GeekQuiz.Models;
    
  7. Dodaj następujący kod na początku metody Application_Start , aby ustawić element TriviaDatabaseInitializer jako inicjator bazy danych.

    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. Zmodyfikuj kontroler główny , aby ograniczyć dostęp do uwierzytelnionych użytkowników. W tym celu otwórz plik HomeController.cs w folderze Controllers i dodaj atrybut Authorize do definicji klasy HomeController .

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

    Uwaga / Notatka

    Filtr Autoryzuj sprawdza, czy użytkownik jest uwierzytelniony. Jeśli użytkownik nie jest uwierzytelniony, zwraca kod stanu HTTP 401 (Brak autoryzacji) bez wywoływania akcji. Filtr można zastosować globalnie, na poziomie kontrolera lub na poziomie poszczególnych akcji.

  9. Teraz dostosujesz układ stron internetowych i znakowanie. W tym celu otwórz plik _Layout.cshtml wewnątrz folderu Views | Shared i zaktualizuj zawartość elementu <c3/></c4>, zastępując ciąg <c5>My ASP.NET Application</c5> ciągiem <c6>Geek Quiz</c6>.</p> <pre><code class="lang-cshtml" name="Main"><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> </code></pre></li> <li><p>W tym samym pliku zaktualizuj pasek nawigacyjny, usuwając linki <em>Informacje</em> i <em>Kontakt</em> oraz zmieniając nazwę linku <em>Strona główna</em> do <em>odtwarzania</em>. Ponadto zmień nazwę linku <em>Nazwa aplikacji</em> na <em>Geek Quiz</em>. Kod HTML paska nawigacyjnego powinien wyglądać podobnie do poniższego kodu.</p> <pre><code class="lang-cshtml" name="Main"><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> </code></pre></li> <li><p>Zaktualizuj stopkę strony układu, zastępując ciąg <em>My ASP.NET Application</em> tekstem <em>Geek Quiz</em>. W tym celu zastąp zawartość <strong><elementu stopki></strong> następującym wyróżnionym kodem.</p> <pre><code class="lang-html" name="Main"><div class="container body-content"> @RenderBody() <hr /> <footer> <p>&copy; @DateTime.Now.Year - Geek Quiz</p> </footer> </div> </code></pre></li> </ol> <p><a id="Ex1Task2"></a></p> <h4 id="task-2--creating-the-triviacontroller-web-api">Zadanie 2 — tworzenie internetowego interfejsu API TriviaController</h4> <p>W poprzednim zadaniu utworzono początkową strukturę aplikacji internetowej Geek Quiz. Teraz utworzysz prostą usługę internetowego interfejsu API, która współdziała z modelem danych quizu i udostępnia następujące akcje:</p> <ul> <li> <strong>GET /api/trivia</strong>: pobiera następne pytanie z listy quizów, na które ma odpowiedzieć uwierzytelniony użytkownik.</li> <li> <strong>POST /api/trivia</strong>: przechowuje odpowiedź quizu określoną przez uwierzytelnioowanego użytkownika.</li> </ul> <p>Użyjesz narzędzi do tworzenia szkieletów ASP.NET udostępnionych przez program Visual Studio, aby utworzyć punkt odniesienia dla klasy kontrolera interfejsu API sieci Web.</p> <ol> <li><p>Otwórz plik <strong>WebApiConfig.cs</strong> w folderze <strong>App_Start</strong> . Ten plik definiuje konfigurację usługi internetowego interfejsu API, na przykład sposób mapowania tras na akcje kontrolera internetowego interfejsu API.</p> </li> <li><p>Dodaj na początku pliku poniższą instrukcję using.</p> <pre><code class="lang-csharp" name="Main">using Newtonsoft.Json.Serialization; </code></pre></li> <li><p>Dodaj następujący wyróżniony kod do metody <strong>Register</strong>, aby globalnie skonfigurować formatowanie dla danych JSON pobranych przez metody akcji Web API.</p> <pre><code class="lang-csharp" name="Main">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 } ); } } </code></pre> <div class="NOTE"> <p>Uwaga / Notatka</p> <p> <strong>CamelCasePropertyNamesContractResolver</strong> automatycznie konwertuje nazwy właściwości na przypadek <em>camel</em>, który jest ogólną konwencją nazw właściwości w języku JavaScript.</p> </div> </li> <li><p>W <strong>Eksploratorze rozwiązań</strong> kliknij prawym przyciskiem myszy folder <strong>Controllers</strong> projektu <strong>GeekQuiz</strong> i wybierz polecenie <strong>Dodaj | Nowy element szkieletowy...</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image6.png" alt="Tworzenie nowego elementu szkieletowego" title="Tworzenie nowego elementu szkieletowego" data-linktype="relative-path"/> </p> <p> <em>Tworzenie nowego elementu szkieletowego</em></p> </li> <li><p>W oknie dialogowym <strong>Dodawanie szkieletu</strong> upewnij się, że w okienku po lewej stronie <strong>wybrano wspólny węzeł</strong> . Następnie wybierz szablon <strong>Web API 2 Controller - Pusty</strong> w środkowym okienku i kliknij <strong>Dodaj</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image7.png" alt="Wybieranie pustego szablonu kontrolera Web API 2" title="Wybieranie pustego szablonu kontrolera Web API 2" data-linktype="relative-path"/> </p> <p> <em>Wybieranie szablonu Pusty dla kontrolera Web API 2</em></p> <div class="NOTE"> <p>Uwaga / Notatka</p> <p> <strong>ASP.NET Scaffolding</strong> to framework do generowania kodu dla aplikacji internetowych ASP.NET. Program Visual Studio 2013 zawiera wstępnie zainstalowane generatory kodu dla projektów MVC i internetowego interfejsu API. Należy używać szkieletów w projekcie, gdy chcesz szybko dodać kod współdziałający z modelami danych, aby skrócić czas potrzebny na opracowanie standardowych operacji na danych.</p> <p>Proces tworzenia szkieletu gwarantuje również, że wszystkie wymagane zależności zostaną zainstalowane w projekcie. Jeśli na przykład zaczniesz od pustego projektu ASP.NET, a następnie użyjesz szkieletu, aby dodać kontroler internetowego interfejsu API, wymagane pakiety NuGet i odwołania interfejsu API sieci Web zostaną automatycznie dodane do projektu.</p> </div> </li> <li><p>W oknie dialogowym <strong>Dodawanie kontrolera</strong> wpisz <em>TriviaController</em> w polu tekstowym <strong>Nazwa kontrolera</strong> i kliknij przycisk <strong>Dodaj</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image8.png" alt="Dodawanie kontrolera Trivia" title="Dodawanie kontrolera Trivia" data-linktype="relative-path"/> </p> <p> <em>Dodawanie kontrolera Trivia</em></p> </li> <li><p>Plik <strong>TriviaController.cs</strong> jest następnie dodawany do folderu <strong>Controllers</strong> projektu <strong>GeekQuiz</strong> zawierającego pustą klasę <strong>TriviaController</strong> . Dodaj następujące instrukcje using na początku pliku.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex1 — TriviaControllerUsings</em>)</p> <pre><code class="lang-csharp" name="Main">using System.Data.Entity; using System.Threading; using System.Threading.Tasks; using System.Web.Http.Description; using GeekQuiz.Models; </code></pre></li> <li><p>Dodaj następujący kod na początku klasy <strong>TriviaController</strong> , aby zdefiniować, zainicjować i usunąć wystąpienie <strong>TriviaContext</strong> w kontrolerze.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex1 — TriviaControllerContext</em>)</p> <pre><code class="lang-csharp" name="Main">public class TriviaController : ApiController { private TriviaContext db = new TriviaContext(); protected override void Dispose(bool disposing) { if (disposing) { this.db.Dispose(); } base.Dispose(disposing); } } </code></pre> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Metoda <strong>Dispose</strong><strong>TriviaController</strong> wywołuje metodę <strong>Dispose</strong> wystąpienia <strong>TriviaContext</strong>, co gwarantuje, że wszystkie zasoby używane przez obiekt kontekstowy są zwalniane po zlikwidowaniu wystąpienia <strong>TriviaContext</strong> lub przez mechanizm usuwania śmieci. Obejmuje to zamknięcie wszystkich połączeń bazy danych otwartych przez program Entity Framework.</p> </div> </li> <li><p>Dodaj następującą metodę pomocnika na końcu klasy <strong>TriviaController</strong> . Ta metoda pobiera następujące pytanie quizowe z bazy danych, na które ma odpowiedzieć określony użytkownik.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex1 — TriviaControllerNextQuestion</em>)</p> <pre><code class="lang-csharp" name="Main">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); } </code></pre></li> <li><p>Dodaj następującą metodę akcji <strong>Get</strong> do klasy <strong>TriviaController</strong> . Ta metoda akcji wywołuje metodę pomocnika <strong>NextQuestionAsync</strong> zdefiniowaną w poprzednim kroku, aby pobrać następne pytanie dla uwierzytelnioowanego użytkownika.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex1 — TriviaControllerGetAction</em>)</p> <pre><code class="lang-csharp" name="Main">// 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); } </code></pre></li> <li><p>Dodaj następującą metodę pomocnika na końcu klasy <strong>TriviaController</strong> . Ta metoda przechowuje określoną odpowiedź w bazie danych i zwraca wartość logiczną wskazującą, czy odpowiedź jest poprawna.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex1 — TriviaControllerStoreAsync</em>)</p> <pre><code class="lang-csharp" name="Main">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; } </code></pre></li> <li><p>Dodaj następującą metodę akcji <strong>Post</strong> do klasy <strong>TriviaController</strong> . Ta metoda akcji kojarzy odpowiedź z uwierzytelnionymi użytkownikami i wywołuje metodę pomocnika <strong>StoreAsync</strong> . Następnie wysyła odpowiedź z wartością logiczną zwracaną przez metodę pomocnika.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex1 — TriviaControllerPostAction</em>)</p> <pre><code class="lang-csharp" name="Main">// 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); } </code></pre></li> <li><p>Zmodyfikuj kontroler interfejsu API sieci Web, aby ograniczyć dostęp do uwierzytelnionych użytkowników, dodając atrybut <strong>Authorize</strong> do definicji klasy <strong>TriviaController</strong> .</p> <pre><code class="lang-csharp" name="Main">[Authorize] public class TriviaController : ApiController { ... } </code></pre></li> </ol> <p><a id="Ex1Task3"></a></p> <h4 id="task-3--running-the-solution">Zadanie 3 — uruchamianie rozwiązania</h4> <p>W tym zadaniu sprawdzisz, czy usługa internetowego interfejsu API utworzona w poprzednim zadaniu działa zgodnie z oczekiwaniami. Użyjesz narzędzi deweloperskich F12 Internet Explorera do przechwytywania ruchu sieciowego i sprawdzania pełnej odpowiedzi z usługi Web API.</p> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Upewnij się, że program <strong>Internet Explorer</strong> został wybrany w przycisku <strong>Uruchom</strong> znajdujący się na pasku narzędzi programu Visual Studio.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image9.png" alt="Opcja programu Internet Explorer" data-linktype="relative-path"/> </p> </div> <ol> <li><p>Naciśnij <strong>klawisz F5</strong> , aby uruchomić rozwiązanie. Strona <strong>Logowanie</strong> powinna pojawić się w przeglądarce.</p> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Po uruchomieniu aplikacji zostanie wyzwolona domyślna trasa MVC, która domyślnie jest mapowana na akcję <strong>Indeks</strong> klasy <strong>HomeController</strong> . Ponieważ <strong>funkcja HomeController</strong> jest ograniczona do uwierzytelnionych użytkowników (pamiętaj, że ta klasa została ozdobiona atrybutem <strong>Authorize</strong> w ćwiczeniu 1), a użytkownik nie jest jeszcze uwierzytelniony, aplikacja przekierowuje oryginalne żądanie do strony logowania.</p> </div> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image10.png" alt="Uruchamianie rozwiązania" title="Uruchamianie rozwiązania" data-linktype="relative-path"/> </p> <p> <em>Uruchamianie rozwiązania</em></p> </li> <li><p>Kliknij przycisk <strong>Zarejestruj,</strong> aby utworzyć nowego użytkownika.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image11.png" alt="Rejestrowanie nowego użytkownika" title="Rejestrowanie nowego użytkownika" data-linktype="relative-path"/> </p> <p> <em>Rejestrowanie nowego użytkownika</em></p> </li> <li><p>Na stronie <strong>Rejestrowanie</strong> wprowadź <strong>nazwę użytkownika</strong> i <strong>hasło</strong>, a następnie kliknij przycisk <strong>Zarejestruj</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image12.png" alt="Strona rejestracji" title="Strona rejestracji" data-linktype="relative-path"/> </p> <p> <em>Strona rejestrowania</em></p> </li> <li><p>Aplikacja rejestruje nowe konto, a użytkownik jest uwierzytelniany i przekierowywany z powrotem do strony głównej.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image13.png" alt="Użytkownik jest uwierzytelniony" title="Użytkownik uwierzytelniony" data-linktype="relative-path"/> </p> <p> <em>Użytkownik jest uwierzytelniany</em></p> </li> <li><p>W przeglądarce naciśnij klawisz <strong>F12</strong> , aby otworzyć panel <strong>Narzędzia deweloperskie</strong> . Naciśnij <strong>klawisze CTRL + 4</strong> lub kliknij ikonę <strong>Sieć</strong> , a następnie kliknij zielony przycisk strzałki, aby rozpocząć przechwytywanie ruchu sieciowego.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image14.png" alt="Inicjowanie przechwytywania sieci Web interfejsu API" title="Inicjowanie przechwytywania sieci Web interfejsu API" data-linktype="relative-path"/> </p> <p> <em>Inicjowanie przechwytywania interfejsu API sieci Web</em></p> </li> <li><p>Dołącz <strong>api/trivia</strong> do adresu URL w pasku adresu przeglądarki. Teraz sprawdzisz szczegóły odpowiedzi z metody <strong>Get</strong> action w <strong>triviaController</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image15.png" alt="Pobieranie danych następnego pytania przez Web API" title="pobieranie danych następnego pytania przez Web API" data-linktype="relative-path"/> </p> <p> <em>Pobieranie danych kolejnego pytania za pomocą internetowego interfejsu API</em></p> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Po zakończeniu pobierania zostanie wyświetlony monit o wykonanie akcji z pobranym plikiem. Pozostaw otwarte okno dialogowe, aby móc oglądać zawartość odpowiedzi za pomocą okna Narzędzia deweloperów.</p> </div> </li> <li><p>Teraz sprawdzisz treść odpowiedzi. Aby to zrobić, kliknij kartę <strong>Szczegóły</strong> , a następnie kliknij pozycję <strong>Treść odpowiedzi</strong>. Możesz sprawdzić, czy pobrane dane są obiektem z <strong>opcjami</strong> właściwości (czyli listą obiektów <strong>TriviaOption</strong> ), <strong>identyfikatorem</strong> i <strong>tytułem</strong> odpowiadającym klasie <strong>TriviaQuestion</strong> .</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image16.png" alt="Wyświetlanie treści odpowiedzi internetowego interfejsu API" title="Wyświetlanie treści odpowiedzi internetowego interfejsu API" data-linktype="relative-path"/> </p> <p> <em>Wyświetlanie treści odpowiedzi Web API</em></p> </li> <li><p>Wróć do programu Visual Studio i naciśnij <strong>SHIFT + F5</strong> , aby zatrzymać debugowanie.</p> </li> </ol> <p><a id="Exercise2"></a></p> <h3 id="exercise-2-creating-the-spa-interface">Ćwiczenie 2. Tworzenie interfejsu SPA</h3> <p>W tym ćwiczeniu najpierw utworzysz część front-endu aplikacji internetowej Geek Quiz, koncentrując się na interakcji aplikacji typu Single-Page przy użyciu AngularJS. Następnie ulepszysz środowisko użytkownika z css3, aby wykonać rozbudowane animacje i zapewnić wizualny efekt przełączania kontekstu podczas przechodzenia z jednego pytania do następnego.</p> <p><a id="Ex2Task1"></a></p> <h4 id="task-1--creating-the-spa-interface-using-angularjs">Zadanie 1 — tworzenie interfejsu SPA przy użyciu platformy AngularJS</h4> <p>W tym zadaniu użyjesz platformy <strong>AngularJS</strong> do zaimplementowania strony klienta aplikacji Geek Quiz. <strong>AngularJS</strong> to platforma Języka JavaScript typu open source, która rozszerza aplikacje oparte na przeglądarce za pomocą funkcji <em>Model-View-Controller</em> (MVC), ułatwiając tworzenie i testowanie.</p> <p>Rozpoczniesz od zainstalowania platformy AngularJS z konsoli menedżera pakietów programu Visual Studio. Następnie utworzysz kontroler, aby obsłużyć działanie aplikacji Geek Quiz, oraz widok do renderowania pytań i odpowiedzi quizowych przy użyciu szablonów AngularJS.</p> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Aby uzyskać więcej informacji na temat platformy AngularJS, zobacz [<a href="http://angularjs.org/" data-linktype="external">http://angularjs.org/</a>](<a href="http://angularjs.org/" data-linktype="external">http://angularjs.org/</a>).</p> </div> <ol> <li><p>Otwórz program <strong>Visual Studio Express 2013 for Web</strong> i otwórz rozwiązanie <strong>GeekQuiz.sln</strong> znajdujące się w folderze <strong>Source/Ex2-CreatingASPAInterface/Begin</strong> . Alternatywnie możesz kontynuować pracę z rozwiązaniem uzyskanym w poprzednim ćwiczeniu.</p> </li> <li><p>Otwórz <strong>Konsolę Menedżera Pakietów</strong> z <strong>Narzędzia</strong>><strong>Menedżer Pakietów NuGet</strong>. Wpisz następujące polecenie, aby zainstalować pakiet NuGet <strong>AngularJS.Core</strong> .</p> <pre><code class="lang-powershell" name="Main">Install-Package AngularJS.Core </code></pre></li> <li><p>W <strong>Eksploratorze rozwiązań</strong> kliknij prawym przyciskiem myszy folder <strong>Scripts</strong> projektu <strong>GeekQuiz</strong> i wybierz pozycję <strong>Dodaj | Nowy folder</strong>. Nazwij folder <strong>app</strong> i naciśnij <strong>Enter</strong>.</p> </li> <li><p>Kliknij prawym przyciskiem myszy właśnie utworzony folder <strong>aplikacji</strong> i wybierz polecenie <strong>Dodaj | Plik JavaScript</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image17.png" alt="Tworzenie nowego pliku JavaScript" data-linktype="relative-path"/> </p> <p> <em>Tworzenie nowego pliku JavaScript</em></p> </li> <li><p>W oknie dialogowym <strong>Określanie nazwy elementu</strong> wpisz <em>quiz-controller</em> w polu tekstowym <strong>Nazwa elementu</strong> i kliknij <strong>przycisk OK</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image18.png" alt="Nazywanie nowego pliku JavaScript" data-linktype="relative-path"/> </p> <p> <em>Nazywanie nowego pliku JavaScript</em></p> </li> <li><p>W pliku <strong>quiz-controller.js</strong> dodaj następujący kod, aby zadeklarować i zainicjować kontroler AngularJS <strong>QuizCtrl</strong> .</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex2 — AngularQuizController</em>)</p> <pre><code class="lang-javascript" name="Main">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'; }; }); </code></pre> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Funkcja konstruktora kontrolera <strong>QuizCtrl</strong> oczekuje iniekcyjnego parametru o nazwie <strong>$scope</strong>. Początkowy stan zakresu należy skonfigurować w funkcji konstruktora, dołączając właściwości do obiektu <strong>$scope</strong> . Właściwości zawierają <strong>model widoku</strong> i będą dostępne dla szablonu po zarejestrowaniu kontrolera.</p> <p>Kontroler <strong>QuizCtrl</strong> jest zdefiniowany wewnątrz modułu o nazwie <strong>QuizApp</strong>. Moduły to jednostki pracy, które umożliwiają podzielenie aplikacji na oddzielne składniki. Głównymi zaletami korzystania z modułów jest to, że kod jest łatwiejszy do zrozumienia i ułatwia testowanie jednostkowe, możliwość ponownego użycia i łatwość konserwacji.</p> </div> </li> <li><p>Teraz dodasz zachowanie do zakresu w celu reagowania na zdarzenia wyzwalane z widoku. Dodaj następujący kod na końcu kontrolera <strong>QuizCtrl</strong> , aby zdefiniować funkcję <strong>nextQuestion</strong> w obiekcie <strong>$scope</strong> .</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex2 — AngularQuizControllerNextQuestion</em>)</p> <pre><code class="lang-javascript" name="Main">.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; }); }; }; </code></pre> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Ta funkcja pobiera następne pytanie z internetowego interfejsu API <strong>Trivia</strong> utworzonego w poprzednim ćwiczeniu i dołącza dane pytania do obiektu <strong>$scope</strong> .</p> </div> </li> <li><p>Wstaw następujący kod na końcu kontrolera <strong>QuizCtrl</strong> , aby zdefiniować funkcję <strong>sendAnswer</strong> w obiekcie <strong>$scope</strong> .</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex2 — AngularQuizControllerSendAnswer</em>)</p> <pre><code class="lang-javascript" name="Main">.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; }); }; }; </code></pre> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Ta funkcja wysyła odpowiedź wybraną przez użytkownika do internetowego interfejsu API <strong>Trivia</strong> i zapisuje wynik — tj. jeśli odpowiedź jest poprawna lub nie — w obiekcie <strong>$scope</strong> .</p> <p>Funkcje <strong>nextQuestion</strong> i <strong>sendAnswer</strong> z powyższego używają obiektu AngularJS <strong>$http</strong> do abstrahowania komunikacji z Web API za pośrednictwem obiektu XMLHttpRequest JavaScript. Usługa AngularJS obsługuje inną usługę, która zapewnia wyższy poziom abstrakcji w celu wykonywania operacji CRUD względem zasobu za pośrednictwem interfejsów API RESTful. Obiekt AngularJS <strong>$resource</strong> ma metody akcji, które zapewniają zachowania wysokiego poziomu bez konieczności interakcji z <strong>obiektem $http</strong> . Rozważ użycie obiektu <strong>$resource</strong> w scenariuszach, które wymagają zastosowania modelu CRUD (więcej informacji można znaleźć w <a href="https://docs.angularjs.org/api/ngResource/service/$resource" data-linktype="external">dokumentacji $resource</a>).</p> </div> </li> <li><p>Następnym krokiem jest utworzenie szablonu AngularJS definiującego widok testu. W tym celu otwórz plik <strong>Index.cshtml</strong> wewnątrz <strong>pliku Views | Folder główny</strong> i zastąp zawartość następującym kodem.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex2 — GeekQuizView</em>)</p> <pre><code class="lang-cshtml" name="Main">@{ 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") } </code></pre> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Szablon AngularJS to specyfikacja deklaratywna, która używa informacji z modelu i kontrolera do przekształcania znaczników statycznych w dynamiczny widok widoczny przez użytkownika w przeglądarce. Poniżej przedstawiono przykłady elementów AngularJS i atrybutów elementów, które mogą być używane w szablonie:</p> <ul> <li>Dyrektywa <strong>ng-app</strong> informuje AngularJS o elemencie DOM, który wskazuje na korzeń aplikacji.</li> <li>Dyrektywa <strong>ng-controller</strong> dołącza kontroler do modelu DOM w punkcie, w którym zadeklarowana jest dyrektywa.</li> <li>Notacja nawiasu klamrowego <strong>{{ }}</strong> określa powiązania z właściwościami zakresu zdefiniowanymi w kontrolerze.</li> <li>Dyrektywa <strong>ng-click</strong> służy do wywoływania funkcji zdefiniowanych w zakresie w odpowiedzi na kliknięcia użytkownika.</li> </ul> </div> </li> <li><p>Otwórz plik <strong>Site.css</strong> w folderze <strong>Zawartość</strong> i dodaj następujące wyróżnione style na końcu pliku, aby zapewnić wygląd i działanie widoku testu.</p> <p>(Fragment kodu — <em>AspNetWebApiSpa — Ex2 — GeekQuizStyles</em>)</p> <pre><code class="lang-css" name="Main">.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; } </code></pre></li> </ol> <p><a id="Ex2Task2"></a></p> <h4 id="task-2--running-the-solution">Zadanie 2 — uruchamianie rozwiązania</h4> <p>W tym zadaniu wykonasz rozwiązanie przy użyciu nowego interfejsu użytkownika utworzonego za pomocą platformy AngularJS, aby odpowiedzieć na niektóre pytania testowe.</p> <ol> <li><p>Naciśnij <strong>klawisz F5</strong> , aby uruchomić rozwiązanie.</p> </li> <li><p>Zarejestruj nowe konto użytkownika. W tym celu wykonaj kroki rejestracji opisane w ćwiczeniu 1, zadaniu 3.</p> <div class="NOTE"> <p>Uwaga / Notatka</p> <p>Jeśli używasz rozwiązania z poprzedniego ćwiczenia, możesz zalogować się przy użyciu utworzonego wcześniej konta użytkownika.</p> </div> </li> <li><p>Powinna zostać wyświetlona strona <strong>główna</strong> przedstawiająca pierwsze pytanie testu. Odpowiedz na pytanie, klikając jedną z opcji. Spowoduje to wyzwolenie zdefiniowanej wcześniej funkcji <strong>sendAnswer</strong> , która wysyła wybraną opcję do interfejsu API sieci Web <strong>Trivia</strong> .</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image19.png" alt="Udzielenie odpowiedzi na pytanie" title="" data-linktype="relative-path"/> </p> <p> <em>Odpowiadanie na pytanie</em></p> </li> <li><p>Po kliknięciu jednego z przycisków powinna zostać wyświetlona odpowiedź. Kliknij przycisk <strong>Dalej pytanie</strong> , aby wyświetlić następujące pytanie. Spowoduje to wyzwolenie funkcji <strong>nextQuestion</strong> zdefiniowanej w kontrolerze.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image20.png" alt="Żądanie następnego pytania" title="żądanie następnego pytania" data-linktype="relative-path"/> </p> <p> <em>Żądanie następnego pytania</em></p> </li> <li><p>Powinno zostać wyświetlone następne pytanie. Kontynuuj odpowiadanie na pytania tyle razy, ile chcesz. Po wykonaniu wszystkich pytań należy wrócić do pierwszego pytania.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image21.png" alt="Inne pytanie Inne pytanie" title="" data-linktype="relative-path"/> </p> <p> <em>Następne pytanie</em></p> </li> <li><p>Wróć do programu Visual Studio i naciśnij <strong>SHIFT + F5</strong> , aby zatrzymać debugowanie.</p> </li> </ol> <p><a id="Ex2Task3"></a></p> <h4 id="task-3--creating-a-flip-animation-using-css3">Zadanie 3 — tworzenie animacji przerzucania przy użyciu css3</h4> <p>W tym zadaniu użyjesz właściwości CSS3 do wykonywania rozbudowanych animacji przez dodanie efektu przerzucania po udzieleniu odpowiedzi na pytanie i pobraniu następnego pytania.</p> <ol> <li><p>W <strong>Eksploratorze rozwiązań</strong> kliknij prawym przyciskiem myszy folder <strong>Content</strong> projektu <strong>GeekQuiz</strong> i wybierz pozycję <strong>Dodaj | Istniejący element...</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image22.png" alt="Dodawanie istniejącego elementu do folderu Zawartość" title="Dodawanie istniejącego elementu do folderu Zawartość" data-linktype="relative-path"/> </p> <p> <em>Dodawanie istniejącego elementu do folderu Zawartość</em></p> </li> <li><p>W oknie dialogowym <strong>Dodawanie istniejącego elementu</strong> przejdź do folderu <strong>Source/Assets</strong> i wybierz <strong>pozycję Flip.css</strong>. Kliknij przycisk <strong>Dodaj</strong>.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image23.png" alt="Dodawanie pliku Flip.css z zasobów" title="Dodawanie pliku Flip.css z zasobów" data-linktype="relative-path"/> </p> <p> <em>Dodawanie pliku Flip.css z zasobów</em></p> </li> <li><p>Otwórz właśnie dodany plik <strong>Flip.css</strong> i sprawdź jego zawartość.</p> </li> <li><p>Znajdź komentarz <strong>przekształcenia przerzucania</strong> . Poniższe style, zgodnie z komentarzem, używają transformacji CSS <strong>perspective</strong> i <strong>rotateY</strong>, aby uzyskać efekt "przewracania karty".</p> <pre><code class="lang-css" name="Main">/* 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); } </code></pre></li> <li><p>Znajdź komentarz <strong>ukryj tył okienka podczas przewracania</strong>. Styl poniżej komentarza ukrywa tył twarzy, gdy znajdują się z dala od przeglądarki, ustawiając właściwość CSS <strong>widoczności wstecznej</strong> na <em>ukrytą</em>.</p> <pre><code class="lang-css" name="Main">/* hide back of pane during flip */ .front, .back { -moz-backface-visibility: hidden; -webkit-backface-visibility: hidden; backface-visibility: hidden; } </code></pre></li> <li><p>Otwórz plik <strong>BundleConfig.cs</strong> w folderze <strong>App_Start</strong> i dodaj odwołanie do pliku <strong>Flip.css</strong> w pakiecie stylów <strong>"~/Content/css"</strong></p> <pre><code class="lang-csharp" name="Main">bundles.Add(new StyleBundle("~/Content/css").Include( "~/Content/bootstrap.css", "~/Content/site.css", "~/Content/Flip.css")); </code></pre></li> <li><p>Naciśnij <strong>klawisz F5</strong> , aby uruchomić rozwiązanie i zalogować się przy użyciu poświadczeń.</p> </li> <li><p>Odpowiedz na pytanie, klikając jedną z opcji. Zwróć uwagę na efekt przerzucania podczas przechodzenia między widokami.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image24.png" alt="Odpowiadanie na pytanie z efektem przełączenia" title="Odpowiadanie na pytanie z efektem przełączenia" data-linktype="relative-path"/> </p> <p> <em>Odpowiadanie na pytanie za pomocą efektu odwracania</em></p> </li> <li><p>Kliknij przycisk <strong>Dalej pytanie</strong> , aby pobrać następujące pytanie. Efekt przewracania powinien pojawić się ponownie.</p> <p> <img src="build-a-single-page-application-spa-with-aspnet-web-api-and-angularjs/_static/image25.png" alt="Pobieranie następującego pytania z efektem przerzucania" title="Pobieranie następującego pytania z efektem przerzucania" data-linktype="relative-path"/> </p> <p> <em>Pobieranie następującego pytania z efektem przerzucania</em></p> </li> </ol> <hr/> <p><a id="Summary"></a></p> <h2 id="summary">Podsumowanie</h2> <p>Kończąc to praktyczne laboratorium, nauczyłeś się, jak:</p> <ul> <li>Tworzenie kontrolera internetowego interfejsu API ASP.NET przy użyciu szkieletu ASP.NET</li> <li>Zaimplementuj akcję Get API sieciowego, aby pobrać następne pytanie quizu</li> <li>Zaimplementuj akcję Post interfejsu API sieciowego w celu przechowywania odpowiedzi na quiz</li> <li>Instalowanie platformy AngularJS z konsoli menedżera pakietów programu Visual Studio</li> <li>Implementowanie szablonów i kontrolerów platformy AngularJS</li> <li>Używanie przejść CSS3 do wykonywania efektów animacji</li> </ul>