Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00203 005992 14079351 na godz. na dobę w sumie
AngularJS - ebook/pdf
AngularJS - ebook/pdf
Autor: , Liczba stron: 224
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-9996-4 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> javascript - programowanie
Porównaj ceny (książka, ebook (-20%), audiobook).

Poznaj możliwości AngularJS!

AngularJS to hit ostatnich miesięcy w aplikacjach internetowych, wniósł bowiem do kodu JavaScript powiew świeżości oraz najlepsze praktyki znane z innych języków programowania. Architektura MVC, wstrzykiwane zależności, wiązanie danych to tylko niektóre z cech AngularJS. Jeżeli zaintrygowały Cię jego możliwości i chciałbyś zgłębić potencjał tego rozwiązania, to trafiłeś na doskonałą książkę!

Napisana przez inżynierów Google, pracujących na co dzień przy AngularJS, zawiera najświeższe informacje z pierwszej ręki. Sięgnij po nią i przekonaj się, jak szybko stworzyć łatwą w utrzymaniu aplikację, korzystającą z nowoczesnych wzorców, komunikującą się wydajnie z serwerem oraz pokrytą automatycznymi testami. Zdobędziesz wiedzę na temat dyrektyw, kontrolerów oraz szablonów. Ponadto przekonasz się, jak tworzyć aplikacje wspierające wiele języków narodowych oraz w jaki sposób radzić sobie z ciasteczkami (cookies). Książka ta jest doskonałą lekturą dla wszystkich osób chcących dzięki AngularJS zmienić swoje podejście do tworzonego kodu JavaScript.

Dzięki tej książce:

Twój przewodnik po świecie AngularJS!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: AngularJS Tłumaczenie: Robert Górczyński ISBN: 978-83-246-9990-2 © 2014 Helion S.A. Authorized Polish translation of the English edition AngularJS, ISBN 9781449344856 © 2013 Brad Green and Shyam Seshadri. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie bierze jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Wydawnictwo HELION nie ponosi również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/angula.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/angula Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:316)ci Wprowadzenie ...........................................................................................7 8 8 9 Konwencje zastosowane w ksi(cid:241)(cid:276)ce U(cid:276)ycie przyk(cid:228)adowych kodów Podzi(cid:246)kowania Rozdzia(cid:293) 1. Wprowadzenie do AngularJS ................................................11 12 18 21 Koncepcje Przyk(cid:228)ad — koszyk na zakupy Co dalej? Rozdzia(cid:293) 2. Anatomia aplikacji AngularJS ...............................................23 23 24 27 51 55 57 61 63 65 67 Wywo(cid:228)anie AngularJS Architektura MVC Szablony i do(cid:228)(cid:241)czanie danych Organizacja zale(cid:276)no(cid:264)ci za pomoc(cid:241) modu(cid:228)ów Formatowanie danych za pomoc(cid:241) filtrów Zmiana widoków za pomoc(cid:241) tras i us(cid:228)ugi $location Komunikacja z serwerem U(cid:276)ycie dyrektyw do zmiany elementów drzewa DOM Weryfikacja danych wej(cid:264)ciowych u(cid:276)ytkownika Co dalej? Rozdzia(cid:293) 3. Programowanie w AngularJS ...............................................69 70 73 75 76 79 Organizacja projektu Narz(cid:246)dzia Uruchamianie aplikacji Testowanie w AngularJS Testy jednostkowe 3 Kup książkęPoleć książkę Testy typu E2E/integracji Kompilacja Inne wspania(cid:228)e narz(cid:246)dzia Narz(cid:246)dzie Yeoman — optymalizacja sposobu pracy Integracja AngularJS i RequireJS 80 82 84 88 92 Rozdzia(cid:293) 4. Analiza aplikacji AngularJS .................................................101 101 102 105 122 Aplikacja Relacje mi(cid:246)dzy modelem, kontrolerem i szablonem Kontrolery, dyrektywy i us(cid:228)ugi Testy Rozdzia(cid:293) 5. Komunikacja z serwerami .................................................. 129 129 135 137 143 145 146 147 Komunikacja za pomoc(cid:241) us(cid:228)ugi $http Testy jednostkowe Praca z zasobami RESTful Us(cid:228)uga $q i obietnica Przechwycenie odpowiedzi Kwestie bezpiecze(cid:254)stwa XSRF Rozdzia(cid:293) 6. Dyrektywy ........................................................................... 149 149 150 170 Dyrektywy i weryfikacja kodu HTML Ogólny opis API Co dalej? Rozdzia(cid:293) 7. Inne kwestie .........................................................................171 171 178 Us(cid:228)uga $location Metody modu(cid:228)u AngularJS Komunikacja mi(cid:246)dzy zasi(cid:246)gami za pomoc(cid:241) $on, $emit i $broadcast Ciasteczka Internacjonalizacja i lokalizacja Oczyszczanie kodu HTML i modu(cid:228) Sanitize 182 184 185 188 4 (cid:95) Spis tre(cid:316)ci Kup książkęPoleć książkę Rozdzia(cid:293) 8. (cid:315)ci(cid:233)ga i podpowiedzi ...........................................................191 191 196 201 204 207 210 214 Opakowanie kontrolki jQuery datepicker Lista klubów sportowych — filtrowanie i komunikacja Przekazywanie plików w aplikacji AngularJS U(cid:276)ycie biblioteki Socket.IO Prosta us(cid:228)uga stronicowania Praca z serwerami i logowaniem Podsumowanie Skorowidz ............................................................................................... 216 Spis tre(cid:316)ci (cid:95) 5 Kup książkęPoleć książkę 6 (cid:95) Spis tre(cid:316)ci Kup książkęPoleć książkę ROZDZIA(cid:292) 4. Analiza aplikacji AngularJS W rozdziale 2. przedstawiono pewne najcz(cid:246)(cid:264)ciej u(cid:276)ywane funkcje framewor- ka AngularJS, natomiast w rozdziale 3. zaj(cid:246)li(cid:264)my si(cid:246) zagadnieniami zwi(cid:241)- zanymi ze sposobem prowadzenia prac programistycznych. Zamiast konty- nuowa(cid:232) w(cid:241)tek i podobnie szczegó(cid:228)owo omawia(cid:232) poszczególne funkcje, w tym rozdziale przejdziemy do ma(cid:228)ej, rzeczywistej aplikacji. Na jej pod- stawie dowiesz si(cid:246), jak po(cid:228)(cid:241)czy(cid:232) ze sob(cid:241) omówione dot(cid:241)d fragmenty ca(cid:228)o(cid:264)ci i utworzy(cid:232) rzeczywist(cid:241), dzia(cid:228)aj(cid:241)c(cid:241) aplikacj(cid:246). Zamiast od razu przedstawia(cid:232) ca(cid:228)(cid:241) aplikacj(cid:246), b(cid:246)dziemy j(cid:241) poznawa(cid:232) w ma- (cid:228)ych cz(cid:246)(cid:264)ciach, omawia(cid:232) interesuj(cid:241)ce zagadnienia zwi(cid:241)zane z danym frag- mentem i tym samym powoli budowa(cid:232) kompletn(cid:241) aplikacj(cid:246), która b(cid:246)dzie gotowa, zanim uko(cid:254)czysz lektur(cid:246) rozdzia(cid:228)u. Aplikacja GutHub to prosta aplikacja przeznaczona do zarz(cid:241)dzania przepisami kuli- narnymi. Zosta(cid:228)a zaprojektowana w taki sposób, aby przechowywa(cid:232) przepi- sy kulinarne i jednocze(cid:264)nie pokazywa(cid:232) ró(cid:276)ne interesuj(cid:241)ce aspekty aplikacji AngularJS. Oto cechy charakteryzuj(cid:241)ce tworzon(cid:241) przez nas aplikacj(cid:246): (cid:120) ma uk(cid:228)ad sk(cid:228)adaj(cid:241)cy si(cid:246) z dwóch kolumn; (cid:120) pasek nawigacyjny znajduje si(cid:246) po lewej stronie; (cid:120) pozwala na dodawanie nowych przepisów kulinarnych; (cid:120) umo(cid:276)liwia przegl(cid:241)danie istniej(cid:241)cych przepisów kulinarnych. Widok g(cid:228)ówny aplikacji znajduje si(cid:246) po prawej stronie. W zale(cid:276)no(cid:264)ci od adresu URL ulega ona zmianie i wy(cid:264)wietla list(cid:246) przepisów kulinarnych, 101 Kup książkęPoleć książkę szczegó(cid:228)y dotycz(cid:241)ce konkretnego przepisu lub edytowalny formularz po- zwalaj(cid:241)cy na dodanie nowego b(cid:241)d(cid:274) na edycj(cid:246) istniej(cid:241)cego. Uruchomion(cid:241) aplikacj(cid:246) pokazano na rysunku 4.1. Rysunek 4.1. GutHub, czyli prosta aplikacja przeznaczona do zarz(cid:241)dzania przepisami kulinarnymi Ca(cid:228)a aplikacja jest dost(cid:246)pna w repozytorium GitHub na stronie: https://github. com/shyamseshadri/angularjs-book/tree/master/chapter4. Relacje mi(cid:253)dzy modelem, kontrolerem i szablonem Zanim przejdziemy do omawiania aplikacji, zatrzymajmy si(cid:246) na chwil(cid:246) i zastanówmy, jak trzy fragmenty aplikacji wspó(cid:228)dzia(cid:228)aj(cid:241) ze sob(cid:241) oraz jak powinni(cid:264)my je traktowa(cid:232). Model jest istot(cid:241) aplikacji. Powtórz to zdanie kilkakrotnie. Dzia(cid:228)anie ca(cid:228)ej aplikacji opiera si(cid:246) na modelu, od którego zale(cid:276)(cid:241): wy(cid:264)wietlany widok, dane wy(cid:264)wietlane przez widok, zapisywane informacje i dos(cid:228)ownie wszystko. Warto wi(cid:246)c po(cid:264)wi(cid:246)ci(cid:232) nieco czasu i dok(cid:228)adnie przemy(cid:264)le(cid:232) model — jakie w(cid:228)a(cid:264)ciwo(cid:264)ci powinien mie(cid:232) obiekt modelu, jak b(cid:246)dzie pobierany z serwera, jak b(cid:246)dzie zapisywany i tak dalej. Ze wzgl(cid:246)du na to, (cid:276)e uaktualnienie wido- ku nast(cid:246)puje automatycznie dzi(cid:246)ki u(cid:276)yciu wi(cid:241)zania danych, uwag(cid:246) nale(cid:276)y skoncentrowa(cid:232) na modelu. 102 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Kontroler zawiera logik(cid:246) biznesow(cid:241) i okre(cid:264)la mi(cid:246)dzy innymi: jak b(cid:246)dzie po- bierany model, jakie b(cid:246)d(cid:241) rodzaje operacji przeprowadzanych na modelu, ja- kich informacji widok potrzebuje z modelu, a tak(cid:276)e jak przekszta(cid:228)ci(cid:232) model, aby uzyska(cid:232) potrzebne dane. Ponadto przeprowadzanie weryfikacji, wy- konywanie wywo(cid:228)a(cid:254) do serwera, umieszczanie odpowiednich danych w wi- doku oraz w(cid:228)a(cid:264)ciwie wszystko inne powi(cid:241)zane z wymienionymi dzia(cid:228)ania- mi to równie(cid:276) aktywno(cid:264)(cid:232) definiowana w kontrolerze. I na koniec szablon okre(cid:264)la sposób prezentacji modelu oraz interakcji u(cid:276)yt- kownika z aplikacj(cid:241). Zadania wykonywane przez szablon powinny by(cid:232) ogra- niczone do wymienionych poni(cid:276)ej: (cid:120) wy(cid:264)wietlanie modelu; (cid:120) definiowanie sposobów, na jakie u(cid:276)ytkownik mo(cid:276)e korzysta(cid:232) z aplikacji — klikni(cid:246)cia, pola danych wej(cid:264)ciowych i tak dalej; (cid:120) nadawanie stylu aplikacji oraz okre(cid:264)lanie, jak i kiedy pewne elementy maj(cid:241) by(cid:232) wy(cid:264)wietlane — pokazywanie lub ukrywanie i tak dalej; (cid:120) filtrowanie i formatowanie danych (zarówno wej(cid:264)ciowych, jak i wyj- (cid:264)ciowych). Trzeba pami(cid:246)ta(cid:232), (cid:276)e szablon w AngularJS niekoniecznie jest widokiem w architekturze MVC (model – widok – kontroler). Zamiast tego widok jest skompilowan(cid:241) wersj(cid:241) wykonywanego szablonu, rodzajem po(cid:228)(cid:241)czenia sza- blonu i modelu. W szablonie nie nale(cid:276)y umieszcza(cid:232) (cid:276)adnego rodzaju logiki biznesowej ani definiowa(cid:232) zachowania — tego rodzaju dane powinny znajdowa(cid:232) si(cid:246) w kontrolerze. Zachowanie prostoty szablonów pozwala na w(cid:228)a(cid:264)ciw(cid:241) sepa- racj(cid:246) obowi(cid:241)zków, a ponadto na przetestowanie wi(cid:246)kszo(cid:264)ci kodu za po- moc(cid:241) jedynie testów jednostkowych. Szablony powinny by(cid:232) testowane za pomoc(cid:241) testów scenariuszy. W tym miejscu móg(cid:228)by(cid:264) zapyta(cid:232): gdzie nale(cid:276)y umieszcza(cid:232) polecenia od- powiedzialne za modyfikacje obiektowego modelu dokumentu? Operacje na elementach drzewa DOM nie powinny by(cid:232) definiowane w kontrolerach lub szablonach. Najlepszym miejscem dla nich s(cid:241) dyrektywy AngularJS, cho(cid:232) czasami wspomniane operacje mog(cid:241) by(cid:232) stosowane za pomoc(cid:241) us(cid:228)ug, co pozwala na unikni(cid:246)cie powielania kodu. Przyk(cid:228)ad takiego rozwi(cid:241)zania w aplikacji GutHub równie(cid:276) zostanie zaprezentowany i omówiony. Bez zb(cid:246)dnych ceregieli przechodzimy wi(cid:246)c do modelu. Relacje mi(cid:253)dzy modelem, kontrolerem i szablonem (cid:95) 103 Kup książkęPoleć książkę Model W omawianej aplikacji staramy si(cid:246) zachowa(cid:232) maksymaln(cid:241) prostot(cid:246) modelu — b(cid:246)d(cid:241) to po prostu przepisy kulinarne. Wspomniane przepisy to jedyny obiekt modelu w ca(cid:228)ej aplikacji. Wszystkie pozosta(cid:228)e komponenty zostan(cid:241) zbudowane wokó(cid:228) modelu. Ka(cid:276)dy przepis sk(cid:228)ada si(cid:246) z nast(cid:246)puj(cid:241)cych w(cid:228)a(cid:264)ciwo(cid:264)ci: (cid:120) identyfikator, je(cid:264)li przepis zosta(cid:228) zapisany na serwerze, (cid:120) nazwa, (cid:120) krótki opis, (cid:120) sposób przygotowania, (cid:120) informacje o ewentualnym wyró(cid:276)nieniu przepisu, (cid:120) tablica sk(cid:228)adników podanych w postaci nazwy, ilo(cid:264)ci i jednostki miary. I to tyle, model jest niezwykle prosty. Pozosta(cid:228)e komponenty aplikacji utwo- rzymy na podstawie wymienionego modelu. Poni(cid:276)ej przedstawiono przy- k(cid:228)adowy przepis kulinarny w formacie JSON (przepis ten zosta(cid:228) pokazany na rysunku 4.1 we wcze(cid:264)niejszej cz(cid:246)(cid:264)ci rozdzia(cid:228)u): { id : 1 , title : Ciasteczka , description : Wyborne, chrupi(cid:200)ce na zewn(cid:200)trz, ci(cid:200)gliwe + w (cid:258)rodku i ociekaj(cid:200)ce pyszn(cid:200) czekolad(cid:200) + ciasteczka. Najlepsze w swoim rodzaju. , ingredients : [ { amount : 1 , amountUnits : opakowanie , ingredientName : Chips Ahoy } ], instructions : 1. Kup opakowanie Chips Ahoy\n + 2. Podgrzej ciasteczka w piekarniku\n + 3. Rozsmakuj si(cid:218) w ciep(cid:239)ych ciasteczkach\n + 4. Z innego (cid:283)ród(cid:239)a naucz si(cid:218), jak wypieka(cid:202) wyborne ciasteczka } W celu zachowania prostoty przyk(cid:228)adu nie b(cid:246)dziemy zajmowa(cid:232) si(cid:246) serwe- rem, z którego przepisy kulinarne s(cid:241) pobierane lub w którym s(cid:241) zapisy- wane. Kod serwera znajduje si(cid:246) w repozytorium w serwisie GitHub, a do jego uruchomienia s(cid:228)u(cid:276)y polecenie node web-server.js, które trzeba wyda(cid:232) z poziomu podstawowego katalogu aplikacji GutHub. Przechodzimy teraz do znacznie bardziej skomplikowanych funkcji interfejsu u(cid:276)ytkownika, jakie mo(cid:276)na utworzy(cid:232) na postawie naszego prostego modelu. 104 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Kontrolery, dyrektywy i us(cid:293)ugi Wreszcie mo(cid:276)emy zaj(cid:241)(cid:232) si(cid:246) ciekawszymi aspektami tworzonej przez nas aplikacji. W pierwszej kolejno(cid:264)ci spojrzymy na kod dyrektyw i us(cid:228)ug i po- wiemy sobie nieco o sposobie jego dzia(cid:228)ania. Nast(cid:246)pnie przejdziemy do wielu kontrolerów niezb(cid:246)dnych do zapewnienia prawid(cid:228)owego dzia(cid:228)ania tworzonej aplikacji. Us(cid:293)ugi Poni(cid:276)ej przedstawiono kod (cid:274)ród(cid:228)owy us(cid:228)ug. // Plik: app/scripts/services/services.js. var services = angular.module( guthub.services , [ ngResource ]); services.factory( Recipe , [ $resource , function($resource) { return $resource( /recipes/:id , {id: @id }); }]); services.factory( MultiRecipeLoader , [ Recipe , $q , function(Recipe, $q) { return function() { var delay = $q.defer(); Recipe.query(function(recipes) { delay.resolve(recipes); }, function() { delay.reject( Nie mo(cid:285)na pobra(cid:202) przepisów kulinarnych. ); }); return delay.promise; }; }]); services.factory( RecipeLoader , [ Recipe , $route , $q , function(Recipe, $route, $q) { return function() { var delay = $q.defer(); Recipe.get({id: $route.current.params.recipeId}, function(recipe) { delay.resolve(recipe); }, function() { delay.reject( Nie mo(cid:285)na pobra(cid:202) przepisu + $route.current.params.recipeId); }); return delay.promise; }; }]); Najpierw zajmiemy si(cid:246) us(cid:228)ugami. Nie jest to nasze pierwsze spotkanie z us(cid:228)ugami — zetkn(cid:246)li(cid:264)my si(cid:246) z nimi ju(cid:276) w rozdziale 2. Tutaj zostan(cid:241) omó- wione nieco dok(cid:228)adniej. W przedstawionym pliku znajduj(cid:241) si(cid:246) trzy us(cid:228)ugi AngularJS. Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 105 Kup książkęPoleć książkę Istnieje us(cid:228)uga przepisu kulinarnego, która zwraca tak zwany AngularJS Resource. Jest to zasób RESTful prowadz(cid:241)cy do serwera RESTful. Wspo- mniany zasób hermetyzuje dzia(cid:228)aj(cid:241)c(cid:241) na niskim poziomie us(cid:228)ug(cid:246) $http, a tym samym programista musi utworzy(cid:232) jedynie kod odpowiedzialny za prac(cid:246) z obiektami. Za pomoc(cid:241) tylko pojedynczego wiersza kodu (return $resource) oraz oczywi- (cid:264)cie zale(cid:276)no(cid:264)ci w module guthub.services obiekt Recipe mo(cid:276)e by(cid:232) u(cid:276)yty jako argument w dowolnym kontrolerze — zostanie wówczas wstrzykni(cid:246)ty do wskazanego kontrolera. Co wi(cid:246)cej, ka(cid:276)dy obiekt Recipe ma wbudowane wymienione poni(cid:276)ej metody: (cid:120) Recipe.get(), (cid:120) Recipe.save(), (cid:120) Recipe.query(), (cid:120) Recipe.remove(), (cid:120) Recipe.delete(). Je(cid:276)eli zamierzasz u(cid:276)y(cid:232) metody Recipe.delete() i chcesz zapewni(cid:232) dzia(cid:228)anie aplikacji w przegl(cid:241)darce Internet Explorer, wtedy mu- sisz u(cid:276)y(cid:232) wywo(cid:228)ania w postaci Recipe[ delete ](). Wynika to z faktu, (cid:276)e delete jest s(cid:228)owem kluczowym w przegl(cid:241)darce In- ternet Explorer. Z wymienionych powy(cid:276)ej metod wszystkie poza query() dzia(cid:228)aj(cid:241) z poje- dynczym przepisem kulinarnym. Natomiast warto(cid:264)ci(cid:241) zwrotn(cid:241) metody query() jest domy(cid:264)lnie tablica przepisów kulinarnych. Wiersz kodu deklaruj(cid:241)cy zasób (return $resource) wykonuje równie(cid:276) kilka innych u(cid:276)ytecznych zada(cid:254). 1. Zwró(cid:232) uwag(cid:246) na :id w adresie URL wskazanym dla zasobu RESTful. Wspomniany identyfikator oznacza, (cid:276)e w trakcie wykonywania dowol- nego zapytania (na przyk(cid:228)ad za pomoc(cid:241) Recipe.get()), je(cid:264)li przeka(cid:276)esz obiekt wraz z w(cid:228)a(cid:264)ciwo(cid:264)ci(cid:241) id, wówczas jej warto(cid:264)(cid:232) zostanie umiesz- czona na ko(cid:254)cu adresu URL. Oznacza to, (cid:276)e wywo(cid:228)anie Recipe.get({id: 15}) faktycznie b(cid:246)dzie wy- wo(cid:228)aniem do /recipe/15. 2. Móg(cid:228)by(cid:264) zapyta(cid:232) w tym miejscu: co z drugim obiektem, na przyk(cid:228)ad {id: @id}? Wiersz kodu jest wart tysi(cid:241)ca s(cid:228)ów obja(cid:264)nienia, wi(cid:246)c przejd(cid:274)my od razu do odpowiedniego przyk(cid:228)adu. 106 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Przyjmujemy za(cid:228)o(cid:276)enie, (cid:276)e dost(cid:246)pny jest obiekt Recipe zawieraj(cid:241)cy wszystkie niezb(cid:246)dne informacje, mi(cid:246)dzy innymi warto(cid:264)(cid:232) id. Wspomniany obiekt mo(cid:276)na zapisa(cid:232) za pomoc(cid:241) poni(cid:276)szego fragmentu kodu: // Przyj(cid:266)to za(cid:225)o(cid:298)enie, (cid:298)e obiekt existingRecipeObj ma wszystkie niezb(cid:266)dne w(cid:225)a(cid:286)ciwo(cid:286)ci, // w tym id (na przyk(cid:225)ad o warto(cid:286)ci 13). var recipe = new Recipe(existingRecipeObj); recipe.$save(); Przedstawiony kod spowoduje wykonanie (cid:276)(cid:241)dania POST do /recipe/13. Fragment @id powoduje pobranie warto(cid:264)ci w(cid:228)a(cid:264)ciwo(cid:264)ci id obiektu i u(cid:276)y- cie jej jako parametru id. Takie rozwi(cid:241)zanie przyj(cid:246)to dla wygody — po- zwala ono zaoszcz(cid:246)dzi(cid:232) kilka wierszy kodu. W pliku apps/scripts/services/services.js istniej(cid:241) jeszcze dwie inne us(cid:228)ugi. Obie zaliczaj(cid:241) si(cid:246) do komponentów wczytuj(cid:241)cych: pierwsza (RecipeLoader) wczytuje pojedynczy przepis, natomiast druga (MultiRecipeLoader) jest prze- znaczona do wczytywania wszystkich przepisów kulinarnych. Wymienio- ne us(cid:228)ugi s(cid:241) u(cid:276)ywane podczas konfiguracji tras, a sposób dzia(cid:228)ania tych us(cid:228)ug jest bardzo podobny i zosta(cid:228) przedstawiony poni(cid:276)ej. 1. Utworzenie obiektu wstrzymanego $q (jest to rodzaj obietnicy frameworka AngularJS stosowanej w celu (cid:228)(cid:241)czenia funkcji asynchronicznych). 2. Wykonanie wywo(cid:228)ania do serwera. 3. Okre(cid:264)lenie obiektu wstrzymanego, gdy serwer zwraca warto(cid:264)(cid:232). 4. Zwrot obietnicy, która b(cid:246)dzie u(cid:276)ywana przez mechanizm routingu fra- meworka AngularJS. Obietnice frameworka AngularJS Obietnica to interfejs przeznaczony do pracy z obiektami, które s(cid:241) zwra- cane lub b(cid:246)d(cid:241) wype(cid:228)nione w przysz(cid:228)o(cid:264)ci (w zasadzie s(cid:241) to akcje asyn- chroniczne). Ogólnie rzecz bior(cid:241)c, na obietnic(cid:246) sk(cid:228)ada si(cid:246) obiekt oraz funkcja then(). Aby zobaczy(cid:232) zalety obietnic, spójrzmy na przyk(cid:228)ad, w którym konieczne jest pobranie profilu u(cid:276)ytkownika: var currentProfile = null; var username = dowolnaNazwa ; fetchServerConfig(function(serverConfig) { fetchUserProfiles(serverConfig.USER_PROFILES, username, function(profiles) { currentProfile = profiles.currentProfile; }); }); Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 107 Kup książkęPoleć książkę Z powy(cid:276)szym podej(cid:264)ciem zwi(cid:241)zanych jest kilka problemów. 1. Kod wynikowy b(cid:246)dzie koszmarnie powcinany, zw(cid:228)aszcza je(cid:264)li zajdzie konieczno(cid:264)(cid:232) po(cid:228)(cid:241)czenia kilku wywo(cid:228)a(cid:254). 2. B(cid:228)(cid:246)dy zg(cid:228)aszane mi(cid:246)dzy wywo(cid:228)aniami zwrotnymi i funkcjami maj(cid:241) tendencj(cid:246) do znikania, je(cid:276)eli nie zostan(cid:241) r(cid:246)cznie obs(cid:228)u(cid:276)one na ka(cid:276)dym etapie. 3. W wewn(cid:246)trznym wywo(cid:228)aniu zwrotnym konieczna jest hermetyza- cja logiki zwi(cid:241)zanej z dzia(cid:228)aniami przeprowadzanymi za pomoc(cid:241) zmiennej currentProfile bezpo(cid:264)rednio lub za pomoc(cid:241) oddzielnej funkcji. Obietnica rozwi(cid:241)zuje wymienione problemy. Zanim si(cid:246) przekonasz, w jaki sposób, najpierw spójrz na ten sam kod, ale zaimplementowany z u(cid:276)yciem obietnic: var currentProfile = fetchServerConfig().then(function(serverConfig) { return fetchUserProfiles(serverConfig.USER_PROFILES, username); }).then(function(profiles) { return profiles.currentProfile; }, function(error) { // Obs(cid:225)uga b(cid:225)(cid:266)dów powsta(cid:225)ych w fetchServerConfig() // lub w fetchUserProfiles(). }); Zwró(cid:232) uwag(cid:246) na zalety nowego rozwi(cid:241)zania. 1. Istnieje mo(cid:276)liwo(cid:264)(cid:232) (cid:228)(cid:241)czenia wywo(cid:228)a(cid:254) funkcji i nie spowoduje to koszmaru zwi(cid:241)zanego ze stosowaniem wci(cid:246)(cid:232) w kodzie. 2. Masz gwarancj(cid:246), (cid:276)e wywo(cid:228)anie poprzedniej funkcji zostanie zako(cid:254)- czone, zanim nast(cid:241)pi wywo(cid:228)anie kolejnej funkcji w (cid:228)a(cid:254)cuchu. 3. Ka(cid:276)de wywo(cid:228)anie then() pobiera dwa argumenty (oba to funkcje). Pierwszy to funkcja wywo(cid:228)ywana w przypadku sukcesu operacji, natomiast drugi to procedura obs(cid:228)ugi b(cid:228)(cid:246)dów. 4. W przypadku wyst(cid:241)pienia b(cid:228)(cid:246)dów w (cid:228)a(cid:254)cuchu wspomniany b(cid:228)(cid:241)d b(cid:246)- dzie propagowany przez pozosta(cid:228)e procedury obs(cid:228)ugi b(cid:228)(cid:246)dów. Dla- tego te(cid:276) b(cid:228)(cid:241)d w dowolnym wywo(cid:228)aniu zwrotnym mo(cid:276)na obs(cid:228)u(cid:276)y(cid:232) na ko(cid:254)cu. Móg(cid:228)by(cid:264) zapyta(cid:232): co z wywo(cid:228)aniami resolve() i reject()? Wywo(cid:228)anie deferred() to we frameworku AngularJS sposób tworzenia obietnic. Z kolei wywo(cid:228)anie resolve() powoduje spe(cid:228)nienie obietnicy (i wywo(cid:228)anie pro- cedury obs(cid:228)ugi w przypadku sukcesu operacji), podczas gdy wywo(cid:228)anie reject powoduje wywo(cid:228)anie procedury obs(cid:228)ugi b(cid:228)(cid:246)dów w obietnicy. 108 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Do tego zagadnienia powrócimy jeszcze podczas konfiguracji tras. Dyrektywy Przechodzimy teraz do dwóch dyrektyw, które b(cid:246)d(cid:241) u(cid:276)ywane w tworzo- nej tutaj aplikacji. butterbar Ta dyrektywa b(cid:246)dzie pokazana lub ukryta w trakcie wczytywania in- formacji przez stron(cid:246), a tak(cid:276)e po zmianie trasy. Jest po(cid:228)(cid:241)czona z me- chanizmem zmiany trasy i automatycznie zostaje ukryta lub umiesz- czona w znaczniku na podstawie stanu strony. focus Ta dyrektywa jest u(cid:276)ywana w celu zagwarantowania, (cid:276)e pewne pola tekstowe (lub elementy) formularza sieciowego s(cid:241) aktywne. Spójrz na przyk(cid:228)adowy fragment kodu: // Plik: app/scripts/directives/directives.js. var directives = angular.module( guthub.directives , []); directives.directive( butterbar , [ $rootScope , function($rootScope) { return { link: function(scope, element, attrs) { element.addClass( hide ); $rootScope.$on( $routeChangeStart , function() { element.removeClass( hide ); }); $rootScope.$on( $routeChangeSuccess , function() { element.addClass( hide ); }); } }; }]); directives.directive( focus , function() { return { link: function(scope, element, attrs) { element[0].focus(); } }; }); Przedstawiona dyrektywa zwraca obiekt wraz z pojedyncz(cid:241) w(cid:228)a(cid:264)ciwo(cid:264)ci(cid:241) link. Dok(cid:228)adne omówienie tematu tworzenia w(cid:228)asnych dyrektyw znajdziesz w rozdziale 6., teraz musisz jedynie wiedzie(cid:232) o dwóch rzeczach. Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 109 Kup książkęPoleć książkę 1. Dyrektywy przechodz(cid:241) przez proces sk(cid:228)adaj(cid:241)cy si(cid:246) z dwóch etapów. Na pierwszym etapie (faza kompilacji) nast(cid:246)puje wyszukanie wszystkich dyrektyw do(cid:228)(cid:241)czonych do elementu drzewa DOM, a nast(cid:246)pnie ich przetworzenie. Wszelkie operacje na elementach drzewa DOM s(cid:241) przeprowadzane na etapie kompilacji. Na ko(cid:254)cu fazy otrzymujesz funk- cj(cid:246) (cid:228)(cid:241)cz(cid:241)c(cid:241). 2. Na drugim etapie (faza (cid:228)(cid:241)czenia — t(cid:246) faz(cid:246) wcze(cid:264)niej wykorzystali(cid:264)my) wygenerowany szablon elementów drzewa DOM jest do(cid:228)(cid:241)czany do za- si(cid:246)gu (scope). Ponadto dodawane s(cid:241) wszelkie komponenty monitoruj(cid:241)ce lub nas(cid:228)uchuj(cid:241)ce, co oznacza powstanie funkcjonuj(cid:241)cego wi(cid:241)zania mi(cid:246)- dzy zasi(cid:246)giem scope i elementem. Wszystko, co jest powi(cid:241)zane z zasi(cid:246)giem scope, zachodzi na etapie (cid:228)(cid:241)czenia. Co si(cid:246) dzieje w naszej dyrektywie? Zajrzyjmy do niej i przekonajmy si(cid:246). Dyrektywa butterbar mo(cid:276)e by(cid:232) u(cid:276)ywana w nast(cid:246)puj(cid:241)cy sposób: div butterbar Komunikat informuj(cid:200)cy o wczytywaniu... /div Dzia(cid:228)anie dyrektywy polega na ukryciu elementu oraz dodaniu dwóch komponentów monitoruj(cid:241)cych zasi(cid:246)g g(cid:228)ówny (scope). Za ka(cid:276)dym razem, gdy rozpoczyna si(cid:246) zmiana trasy, nast(cid:246)puje pokazanie elementu (przez zmian(cid:246) jego klasy), a po zako(cid:254)czonej powodzeniem zmianie trasy mamy ponowne ukrycie dyrektywy butterbar. Interesuj(cid:241)c(cid:241) cech(cid:241), na któr(cid:241) warto tutaj zwróci(cid:232) uwag(cid:246), jest sposób wstrzyk- ni(cid:246)cia $rootScope do dyrektywy. Wszystkie dyrektywy maj(cid:241) bezpo(cid:264)rednie powi(cid:241)zanie z systemem wstrzykiwania zale(cid:276)no(cid:264)ci w AngularJS, co pozwala na wstrzykiwanie do nich us(cid:228)ug oraz innych niezb(cid:246)dnych komponentów. Ostatnia kwestia warta uwagi to API przeznaczone do pracy z elementem. Programi(cid:264)ci przyzwyczajeni do biblioteki jQuery b(cid:246)d(cid:241) szcz(cid:246)(cid:264)liwi, wiedz(cid:241)c, (cid:276)e zastosowanie ma doskonale znana im sk(cid:228)adnia (addClass, removeClass). Framework AngularJS implementuje pewien podzbiór wywo(cid:228)a(cid:254) jQuery, a wi(cid:246)c biblioteka jQuery stanowi opcjonaln(cid:241) zale(cid:276)no(cid:264)(cid:232) dla ka(cid:276)dego projektu AngularJS. Je(cid:276)eli w projekcie chcesz wykorzysta(cid:232) pe(cid:228)ni(cid:246) mo(cid:276)liwo(cid:264)ci ofe- rowanych przez jQuery, wtedy powiniene(cid:264) wiedzie(cid:232), (cid:276)e AngularJS u(cid:276)ywa jej zamiast wbudowanej implementacji. Druga dyrektywa (focus) jest znacznie prostsza. Jej dzia(cid:228)anie polega na wywo- (cid:228)aniu metody focus() dla bie(cid:276)(cid:241)cego elementu. Mo(cid:276)na j(cid:241) wywo(cid:228)a(cid:232) przez doda- nie atrybutu focus do dowolnego elementu danych wej(cid:264)ciowych, na przyk(cid:228)ad: input type= text focus /input Podczas wczytywania strony element automatycznie jest aktywny. 110 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Kontrolery Po zaprezentowaniu dyrektyw i us(cid:228)ug mo(cid:276)esz wreszcie przej(cid:264)(cid:232) do kon- trolerów, których w naszej aplikacji mamy pi(cid:246)(cid:232). Wszystkie zosta(cid:228)y zdefi- niowane w pojedynczym pliku (app/scripts/controllers/controllers.js), ale omó- wimy je tutaj pojedynczo. Przechodzimy wi(cid:246)c do pierwszego kontrolera (ListCtrl), odpowiedzialnego za wy(cid:264)wietlenie listy wszystkich przepisów kulinarnych przechowywanych w systemie. app.controller( ListCtrl , [ $scope , recipes , function($scope, recipes) { $scope.recipes = recipes; }]); Zwró(cid:232) uwag(cid:246) na jedn(cid:241) bardzo wa(cid:276)n(cid:241) kwesti(cid:246) w przypadku omawianego kontrolera: w konstruktorze nie zawiera on (cid:276)adnego kodu dotycz(cid:241)cego nawi(cid:241)zania po(cid:228)(cid:241)czenia z serwerem i pobrania przepisów kulinarnych. Zamiast tego kod zajmuje si(cid:246) obs(cid:228)ug(cid:241) wcze(cid:264)niej pobranych przepisów. By(cid:232) mo(cid:276)e zastanawiasz si(cid:246), jak to zosta(cid:228)o zrobione. Có(cid:276), dok(cid:228)adn(cid:241) odpowied(cid:274) poznasz w sekcji po(cid:264)wi(cid:246)conej routingowi, ale ju(cid:276) teraz mo(cid:276)emy powie- dzie(cid:232), (cid:276)e wi(cid:241)(cid:276)e si(cid:246) to z us(cid:228)ug(cid:241) MultiRecipeLoader. Po prostu o tym pami(cid:246)taj. Po zapoznaniu si(cid:246) z kontrolerem ListCtrl zobaczysz, (cid:276)e pozosta(cid:228)e s(cid:241) ca(cid:228)- kiem podobne do omówionego. Mimo wszystko zaprezentujemy je po kolei, wskazuj(cid:241)c przy tym interesuj(cid:241)ce aspekty: app.controller( ViewCtrl , [ $scope , $location , recipe , function($scope, $location, recipe) { $scope.recipe = recipe; $scope.edit = function() { $location.path( /edit/ + recipe.id); }; }]); Interesuj(cid:241)cym aspektem kontrolera ViewCtrl jest funkcja edycji udost(cid:246)p- niana obiektowi scope. Zamiast pokazywa(cid:232) i ukrywa(cid:232) pola lub stosowa(cid:232) podobne rozwi(cid:241)zanie, kontroler wykorzystuje framework AngularJS i zleca mu wykonanie najtrudniejszych zada(cid:254) (powiniene(cid:264) stosowa(cid:232) takie samo podej(cid:264)cie!). Funkcja edit() po prostu zmienia adres URL na odpowiednik przepisu kulinarnego, a AngularJS zajmuje si(cid:246) reszt(cid:241). Ponadto framework wykrywa zmian(cid:246) adresu URL i wczytuje odpowiedni widok (w trybie edycji b(cid:246)dzie to po prostu dany przepis kulinarny). Wspaniale! Przechodzimy teraz do kontrolera EditCtrl: app.controller( EditCtrl , [ $scope , $location , recipe , function($scope, $location, recipe) { $scope.recipe = recipe; Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 111 Kup książkęPoleć książkę $scope.save = function() { $scope.recipe.$save(function(recipe) { $location.path( /view/ + recipe.id); }); }; $scope.remove = function() { delete $scope.recipe; $location.path( / ); }; }]); W tym kontrolerze nowo(cid:264)ci(cid:241) s(cid:241) metody save() i remove(), które EditCtrl udost(cid:246)pnia obiektowi scope. Metoda save() obiektu scope dzia(cid:228)a zgodnie z oczekiwaniami. Zapisuje bie- (cid:276)(cid:241)cy przepis kulinarny, a po zako(cid:254)czeniu operacji zapisu przekierowuje u(cid:276)ytkownika do widoku wy(cid:264)wietlaj(cid:241)cego ten sam przepis. Funkcja wywo(cid:228)a- nia zwrotnego jest u(cid:276)yteczna, poniewa(cid:276) pozwala na przeprowadzenie pew- nych operacji po zapisie. Istniej(cid:241) dwa sposoby zapisania przepisu. Jeden z nich zosta(cid:228) przedstawio- ny w kodzie i polega na wywo(cid:228)aniu funkcji $scope.recipe.$save(). Takie rozwi(cid:241)zanie jest mo(cid:276)liwe tylko dlatego, (cid:276)e recipe jest obiektem zasobu zwró- conego przez RecipeLoader. Natomiast drugi sposób zapisu to wywo(cid:228)anie: Recipe.save(recipe); Metoda remove() równie(cid:276) nale(cid:276)y do prostych, a jej dzia(cid:228)anie polega na usuni(cid:246)ciu przepisu z obiektu scope oraz przekierowaniu u(cid:276)ytkownika na stron(cid:246) g(cid:228)ówn(cid:241). Zwró(cid:232) uwag(cid:246), (cid:276)e nie powoduje to rzeczywistego usuni(cid:246)cia przepisu kulinarnego z serwera. Wykonanie dodatkowego wywo(cid:228)ania nie powinno by(cid:232) zbyt trudne. Kolejny kontroler nosi nazw(cid:246) NewCtrl: app.controller( NewCtrl , [ $scope , $location , Recipe , function($scope, $location, Recipe) { $scope.recipe = new Recipe({ ingredients: [ {} ] }); $scope.save = function() { $scope.recipe.$save(function(recipe) { $location.path( /view/ + recipe.id); }); }; }]); 112 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Ten kontroler jest niemal dok(cid:228)adnie taki sam jak EditCtrl (jako (cid:232)wiczenie móg(cid:228)by(cid:264) oba wymienione kontrolery po(cid:228)(cid:241)czy(cid:232) w jeden). Jedyna ró(cid:276)nica polega na tym, (cid:276)e pierwszym krokiem w dzia(cid:228)aniu kontrolera NewCtrl jest utworzenie nowego przepisu kulinarnego (wspomniany przepis to zasób, a wi(cid:246)c kontroler ma funkcj(cid:246) save()). Ca(cid:228)a pozosta(cid:228)a funkcjonalno(cid:264)(cid:232) nie ulega zmianie. Ostatni kontroler to IngredientsCtrl. Jest to kontroler specjalny, ale zanim przejdziemy do jego omówienia, spójrz na tworz(cid:241)cy go kod: app.controller( IngredientsCtrl , [ $scope , function($scope) { $scope.addIngredient = function() { var ingredients = $scope.recipe.ingredients; ingredients[ingredients.length] = {}; }; $scope.removeIngredient = function(index) { $scope.recipe.ingredients.splice(index, 1); }; }]); Wszystkie przedstawione dot(cid:241)d kontrolery s(cid:241) po(cid:228)(cid:241)czone z okre(cid:264)lonymi wi- dokami w interfejsie u(cid:276)ytkownika. Pod tym wzgl(cid:246)dem kontroler Ingredient- sCtrl dzia(cid:228)a nieco inaczej. To po prostu kontroler potomny u(cid:276)ywany do edycji stron i hermetyzacji pewnych funkcji niepotrzebnych na ogólnym poziomie. Warto w tym miejscu wspomnie(cid:232) o pewnej interesuj(cid:241)cej kwestii. Skoro to kontroler potomny, dziedziczy obiekt scope po kontrolerze nad- rz(cid:246)dnym (w omawianym przyk(cid:228)adzie jest to kontroler EditCtrl lub NewCtrl). Dlatego te(cid:276) uzyskanie dost(cid:246)pu do obiektu $scope.recipe odbywa si(cid:246) z po- ziomu kontrolera nadrz(cid:246)dnego. Sam kod kontrolera nie zawiera nic szczególnie interesuj(cid:241)cego lub unikalnego. Dodaje kilka nowych sk(cid:228)adników do tablicy sk(cid:228)adników przepisu kulinarnego lub te(cid:276) usuwa okre(cid:264)lony sk(cid:228)adnik z listy. W ten sposób omówili(cid:264)my wszystkie kontrolery. Jedyny fragment kodu JavaScript, jaki pozosta(cid:228) do przeanalizowania, dotyczy konfiguracji routingu: // Plik: app/scripts/controllers/controllers.js. var app = angular.module( guthub , [ guthub.directives , guthub.services ]); app.config([ $routeProvider , function($routeProvider) { $routeProvider. when( / , { controller: ListCtrl , resolve: { recipes: function(MultiRecipeLoader) { return MultiRecipeLoader(); } }, Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 113 Kup książkęPoleć książkę templateUrl: /views/list.html }).when( /edit/:recipeId , { controller: EditCtrl , resolve: { recipe: function(RecipeLoader) { return RecipeLoader(); } }, templateUrl: /views/recipeForm.html }).when( /view/:recipeId , { controller: ViewCtrl , resolve: { recipe: function(RecipeLoader) { return RecipeLoader(); } }, templateUrl: /views/viewRecipe.html }).when( /new , { controller: NewCtrl , templateUrl: /views/recipeForm.html }).otherwise({redirectTo: / }); }]); Zgodnie z wcze(cid:264)niejsz(cid:241) obietnic(cid:241) docieramy do miejsca, w którym u(cid:276)y- wana jest funkcja resolve(). W poprzednim fragmencie kodu skonfigu- rowano modu(cid:228) guthub AngularJS, a tak(cid:276)e trasy i szablony wykorzystywane w aplikacji. Kod (cid:228)(cid:241)czy dyrektywy z utworzonymi przez nas us(cid:228)ugami, a nast(cid:246)pnie wskazuje ró(cid:276)ne trasy, które b(cid:246)d(cid:241) stosowane w aplikacji. Dla ka(cid:276)dej trasy definiowany jest adres URL, kontroler odpowiedzialny za ob- s(cid:228)ug(cid:246) danego adresu, wczytywany szablon, a tak(cid:276)e (wreszcie) obiekt resolve. Obiekt resolve nakazuje frameworkowi AngularJS spe(cid:228)nienie wymaga(cid:254) ka(cid:276)dego klucza, zanim trasa b(cid:246)dzie mog(cid:228)a zosta(cid:232) u(cid:276)yta do wy(cid:264)wietlenia odpowiedniego widoku u(cid:276)ytkownikowi. Zadanie aplikacji polega na wczy- taniu wszystkich przepisów kulinarnych (lub tylko wskazanego), a serwer ma udzieli(cid:232) odpowiedzi przed wy(cid:264)wietleniem strony u(cid:276)ytkownikowi. Do- stawc(cid:246) tras informujemy wi(cid:246)c o posiadaniu przepisów kulinarnych (lub przepisu), a nast(cid:246)pnie podajemy mu sposób, w jaki maj(cid:241) by(cid:232) pobrane dane. W trakcie wykonywania operacji pobierania danych wykorzystywane s(cid:241) dwie us(cid:228)ugi (MultiRecipeLoader i RecipeLoader) zdefiniowane na pocz(cid:241)tku tworzenia aplikacji. Framework AngularJS zosta(cid:228) do(cid:264)(cid:232) sprytnie zaprojek- towany — je(cid:276)eli warto(cid:264)ci(cid:241) zwrotn(cid:241) funkcji resolve() b(cid:246)dzie obietnica An- gularJS, wtedy framework poczeka na spe(cid:228)nienie wspomnianej obietnicy przed przej(cid:264)ciem dalej. Oznacza to konieczno(cid:264)(cid:232) zaczekania, a(cid:276) serwer udzieli odpowiedzi. 114 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Wynik jest w postaci argumentów (o nazwach parametrów b(cid:246)d(cid:241)cych po- lami obiektu) przekazywany konstruktorowi. Na ko(cid:254)cu funkcja otherwise() wskazuje domy(cid:264)lny adres URL dla przekie- rowania, je(cid:264)li nie nast(cid:241)pi dopasowanie (cid:276)adnej trasy. By(cid:232) mo(cid:276)e zauwa(cid:276)y(cid:228)e(cid:264), (cid:276)e kontrolery EditCtrl i NewCtrl korzy- staj(cid:241) z tego samego szablonu, czyli views/recipeForm.html. Co si(cid:246) tutaj dzieje? Po prostu ponownie wykorzystali(cid:264)my szablon przezna- czony do edycji przepisu kulinarnego. Szablon wy(cid:264)wietla ró(cid:276)ne elementy w zale(cid:276)no(cid:264)ci od wywo(cid:228)anego kontrolera. Po zako(cid:254)czeniu omawiania kontrolerów mo(cid:276)emy przej(cid:264)(cid:232) do szablonów. Zobaczysz, w jaki sposób wymienione kontrolery zosta(cid:228)y powi(cid:241)zane z sza- blonami, a tak(cid:276)e dowiesz si(cid:246), jak zarz(cid:241)dza(cid:232) danymi, które s(cid:241) wy(cid:264)wietlane u(cid:276)ytkownikowi. Szablony Rozpoczynamy od zapoznania si(cid:246) z najbardziej zewn(cid:246)trznym, g(cid:228)ównym szablonem zdefiniowanym w pliku index.html. Stanowi on podstaw(cid:246) dla naszej aplikacji sk(cid:228)adaj(cid:241)cej si(cid:246) z pojedynczej strony, a wszystkie pozosta(cid:228)e widoki s(cid:241) wczytywane w kontek(cid:264)cie omawianego tutaj szablonu: !DOCTYPE html html lang= pl ng-app= guthub head title GutHub - tworzenie przepisów kulinarnych i dzielenie si(cid:218) nimi /title script src= scripts/vendor/angular.min.js /script script src= scripts/vendor/angular-resource.min.js /script script src= scripts/directives/directives.js /script script src= scripts/services/services.js /script script src= scripts/controllers/controllers.js /script link href= styles/bootstrap.css rel= stylesheet link href= styles/guthub.css rel= stylesheet /head body header h1 GutHub /h1 /header div butterbar Wczytywanie... /div div class= container-fluid div class= row-fluid div class= span2 !-- Pasek boczny. -- div id= focus a href= #/new Nowy przepis /a /div Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 115 Kup książkęPoleć książkę div a href= #/ Lista przepisów /a /div /div div class= span10 div ng-view /div /div /div /div /body /html W przedstawionym szablonie istnieje pi(cid:246)(cid:232) elementów, na które warto zwró- ci(cid:232) uwag(cid:246). Wi(cid:246)kszo(cid:264)(cid:232) z nich mia(cid:228)e(cid:264) okazj(cid:246) pozna(cid:232) w rozdziale 2. Wspo- mniane elementy omówimy po kolei. ng-app Ustawienie modu(cid:228)u dla aplikacji GutHub. Jest to dok(cid:228)adnie ten sam modu(cid:228), który wykorzystali(cid:264)my we funkcji angular.module(). W ten sposób framework AngularJS wie, jak wszystko ma zosta(cid:232) ze sob(cid:241) po(cid:228)(cid:241)czone. script znacznik W tym miejscu nast(cid:246)puje wczytanie AngularJS w aplikacji. Framework trzeba wczyta(cid:232) przed wszystkimi plikami JavaScript, które go u(cid:276)ywa- j(cid:241). W idealnej sytuacji znaczniki odpowiedzialne za wczytywanie skryptów JavaScript powinny znajdowa(cid:232) si(cid:246) na ko(cid:254)cu pliku szablonu. butterbar Aha! To pierwsze u(cid:276)ycie naszej w(cid:228)asnej dyrektywy. Ta dyrektywa zo- sta(cid:228)a zdefiniowana wcze(cid:264)niej i chcemy j(cid:241) wykorzysta(cid:232) wraz z elemen- tem wy(cid:264)wietlanym podczas zmiany trasy. Po zako(cid:254)czeniu powodze- niem operacji zmiany trasy element powi(cid:241)zany z dyrektyw(cid:241) butterbar powinien zosta(cid:232) ukryty. Dyrektywa powoduje wy(cid:264)wietlenie tekstu (w omawianym przypadku jest to nudny komunikat Wczytywanie...), gdy zachodzi potrzeba. (cid:146)(cid:200)cza href warto(cid:258)ci To (cid:228)(cid:241)cza href do ró(cid:276)nych stron naszej aplikacji sk(cid:228)adaj(cid:241)cej si(cid:246) z poje- dynczej strony. Zwró(cid:232) uwag(cid:246) na u(cid:276)ycie znaku # gwarantuj(cid:241)cego, (cid:276)e strona nie zostanie ponownie wczytana. Adresy s(cid:241) podawane wzgl(cid:246)- dem strony bie(cid:276)(cid:241)cej. Framework AngularJS monitoruje wspomniane adresy URL (dopóki strona nie zostanie ponownie wczytana) i wykonuje ca(cid:228)(cid:241) prac(cid:246) zwi(cid:241)zan(cid:241) z ich obs(cid:228)ug(cid:241) (w rzeczywisto(cid:264)ci jest to bardzo nudne zarz(cid:241)dzanie trasami zdefiniowane przez nas wcze(cid:264)niej wraz z trasami), gdy zachodzi potrzeba. 116 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę ng-view W tym miejscu wykonywana jest pozosta(cid:228)a cz(cid:246)(cid:264)(cid:232) pracy. Wcze(cid:264)niej we fragmencie rozdzia(cid:228)u po(cid:264)wi(cid:246)conym kontrolerom zdefiniowali(cid:264)my trasy. Cz(cid:246)(cid:264)ci(cid:241) definicji jest adres URL trasy, powi(cid:241)zany z ni(cid:241) kontroler i sza- blon. Kiedy framework AngularJS wykryje zmian(cid:246) trasy, wtedy na- st(cid:246)puje wczytanie szablonu, do(cid:228)(cid:241)czenie do niego kontrolera oraz za- st(cid:241)pienie elementu ng-view zawarto(cid:264)ci(cid:241) szablonu. Jedyn(cid:241) rzecz(cid:241) rzucaj(cid:241)c(cid:241) si(cid:246) w oczy jest brak znacznika ng-controller. Wi(cid:246)k- szo(cid:264)(cid:232) aplikacji zawiera pewnego rodzaju kontroler MainController powi(cid:241)zany z szablonem g(cid:228)ównym. Najcz(cid:246)stszym miejscem jego podania jest znacznik body . W omawianej aplikacji nie u(cid:276)ywamy wspomnianego znacznika, poniewa(cid:276) ca(cid:228)y szablon g(cid:228)ówny nie zawiera tre(cid:264)ci AngularJS wymagaj(cid:241)cej odwo(cid:228)ania do obiektu scope. Spójrzmy teraz na szablony powi(cid:241)zane z poszczególnymi kontrolerami. Na pocz(cid:241)tek przygl(cid:241)damy si(cid:246) szablonowi wy(cid:264)wietlaj(cid:241)cemu list(cid:246) przepisów kulinarnych: !-- Plik: chapter4/guthub/app/views/list.html. -- h3 Lista przepisów /h3 ul class= recipes li ng-repeat= recipe in recipes div a ng-href= #/view/{{recipe.id}} {{recipe.title}} /a /div /li /ul To naprawd(cid:246) bardzo nudny szablon. Znajduj(cid:241) si(cid:246) tutaj jedynie dwa intere- suj(cid:241)ce punkty. Pierwszy to standardowy sposób u(cid:276)ycia znacznika ng-repeat. Zadanie wymienionego znacznika polega na pobraniu wszystkich przepisów z obiektu scope, a nast(cid:246)pnie ich wy(cid:264)wietleniu. Drugi interesuj(cid:241)cy punkt to u(cid:276)ycie znacznika ng-href zamiast href. Ma to na celu unikni(cid:246)cie wygenerowania nieprawid(cid:228)owego (cid:228)(cid:241)cza podczas wczyty- wania frameworka AngularJS. Znacznik ng-href gwarantuje, (cid:276)e w (cid:276)adnej chwili u(cid:276)ytkownikowi nie zostanie wy(cid:264)wietlony nieprawid(cid:228)owy znacznik. Wymienionego znacznika powiniene(cid:264) u(cid:276)ywa(cid:232) zawsze, gdy adresy URL s(cid:241) dynamiczne, a nie statyczne. By(cid:232) mo(cid:276)e zadajesz sobie pytanie: gdzie podzia(cid:228) si(cid:246) kontroler? Nie mamy zdefiniowanego znacznika ng-controller i tak naprawd(cid:246) nie ma zdefinio- wanego kontrolera g(cid:228)ównego. W tym miejscu do gry wchodzi mapowanie tras. Mo(cid:276)e pami(cid:246)tasz (mówili(cid:264)my o tym kilka stron wcze(cid:264)niej), (cid:276)e trasa / powoduje przekierowanie do wy(cid:264)wietlaj(cid:241)cego list(cid:246) przepisów kulinarnych szablonu, któremu przypisano kontroler ListCtrl. Dlatego te(cid:276) wszelkie od- niesienia do zmiennych pozostaj(cid:241) w zasi(cid:246)gu wymienionego kontrolera. Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 117 Kup książkęPoleć książkę Teraz przechodzimy do znacznie ciekawszego szablonu, czyli odpowiedzial- nego za wy(cid:264)wietlenie przepisu. !-- Plik: chapter4/guthub/app/views/viewRecipe.html. -- h2 {{recipe.title}} /h2 div {{recipe.description}} /div h3 Sk(cid:239)adniki /h3 span ng-show= recipe.ingredients.length == 0 Brak sk(cid:239)adników /span ul class= unstyled ng-hide= recipe.ingredients.length == 0 li ng-repeat= ingredient in recipe.ingredients span {{ingredient.amount}} /span span {{ingredient.amountUnits}} /span span {{ingredient.ingredientName}} /span /li /ul h3 Sposób przygotowania /h3 div {{recipe.instructions}} /div form ng-submit= edit() class= form-horizontal div class= form-actions button class= btn btn-primary Edycja /button /div /form To kolejny ma(cid:228)y, przydatny szablon. Warto zwróci(cid:232) uwag(cid:246) na dwa punkty powy(cid:276)szego szablonu, cho(cid:232) niekoniecznie w kolejno(cid:264)ci ich wymienienia. Pierwszy to ca(cid:228)kiem standardowy sposób u(cid:276)ycia dyrektywy ng-repeat. Przepisy kulinarne znajduj(cid:241) si(cid:246) w zasi(cid:246)gu kontrolera ViewCtrl wczytanego przez funkcj(cid:246) resolve() przed wy(cid:264)wietleniem strony u(cid:276)ytkownikowi. Dzi(cid:246)ki temu gwarantujemy prawid(cid:228)owe dzia(cid:228)anie strony, gdy zostaje wy(cid:264)wietlona. Drugi punkt to u(cid:276)ycie dyrektywy ng-submit w formularzu. Wymieniona dyrektywa oznacza, (cid:276)e wys(cid:228)anie formularza spowoduje wywo(cid:228)anie funkcji edit() obiektu scope. Wys(cid:228)anie formularza nast(cid:246)puje, gdy klikni(cid:246)ty b(cid:246)dzie przycisk niepowi(cid:241)zany z (cid:276)adn(cid:241) funkcj(cid:241) (w omawianym przypadku to przycisk Edycja). I znów dzia(cid:228)anie frameworka AngularJS zosta(cid:228)o zaprojek- towane bardzo sprytnie — potrafi on prawid(cid:228)owo ustali(cid:232) zasi(cid:246)g, do którego ma si(cid:246) odwo(cid:228)ywa(cid:232) (na przyk(cid:228)ad: modu(cid:228)u, trasy lub kontrolera), i wywo(cid:228)a(cid:232) odpowiedni(cid:241) metod(cid:246) we w(cid:228)a(cid:264)ciwym czasie. Teraz mo(cid:276)emy przej(cid:264)(cid:232) do ostatniego (i prawdopodobnie najbardziej skom- plikowanego) szablonu, czyli formularza pozwalaj(cid:241)cego na dodanie lub edy- cj(cid:246) przepisu kulinarnego. 118 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę !-- Plik: chapter4/guthub/app/views/recipeForm.html. -- h2 Edycja przepisu /h2 form name= recipeForm ng-submit= save() class= form-horizontal div class= control-group label class= control-label for= title Nazwa: /label div class= controls input ng-model= recipe.title class= input-xlarge id= title focus required /div /div div class= control-group label class= control-label for= description Opis: /label div class= controls textarea ng-model= recipe.description class= input-xlarge id= description /textarea /div /div div class= control-group label class= control-label for= ingredients Sk(cid:239)adniki: /label div class= controls ul id= ingredients class= unstyled ng-controller= IngredientsCtrl li ng-repeat= ingredient in recipe.ingredients input ng-model= ingredient.amount class= input-mini input ng-model= ingredient.amountUnits class= input-small input ng-model= ingredient.ingredientName button type= button class= btn btn-mini ng-click= removeIngredient($index) i class= icon-minus-sign /i Usu(cid:241) /button /li button type= button class= btn btn-mini ng-click= addIngredient() i class= icon-plus-sign /i Dodaj /button /ul /div /div div class= control-group label class= control-label for= instructions Sposób przygotowania: /label div class= controls textarea ng-model= recipe.instructions class= input-xxlarge id= instructions /textarea /div /div div class= form-actions button class= btn btn-primary ng-disabled= recipeForm.$invalid Zapisz /button button type= button ng-click= remove() ng-show= !recipe.id class= btn Usu(cid:241) /button /div /form Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 119 Kup książkęPoleć książkę Nie panikuj! Wygl(cid:241)da na to, (cid:276)e szablon zawiera ca(cid:228)kiem spor(cid:241) ilo(cid:264)(cid:232) kodu, i faktycznie tak jest. Jednak po rzeczywistym zag(cid:228)(cid:246)bieniu si(cid:246) we(cid:254) mo(cid:276)na si(cid:246) przekona(cid:232), (cid:276)e kod nie jest skomplikowany. Tak naprawd(cid:246) to prosta, powtarzaj(cid:241)ca si(cid:246) struktura, pokazuj(cid:241)ca, jak edytowalne pola tekstowe zo- sta(cid:228)y zastosowane w formularzu przeznaczonym do edycji przepisów ku- linarnych. (cid:120) W pierwszym polu tekstowym (title) zosta(cid:228)a umieszczona dyrektywa focus. Dzi(cid:246)ki temu po przej(cid:264)ciu na t(cid:246) stron(cid:246) wskazane pole zostanie wybrane, a u(cid:276)ytkownik b(cid:246)dzie móg(cid:228) natychmiast rozpocz(cid:241)(cid:232) wprowa- dzanie danych wej(cid:264)ciowych. (cid:120) Dyrektywa ng-submit jest u(cid:276)yta w bardzo podobny sposób jak w po- przednim przyk(cid:228)adzie, a wi(cid:246)c nie b(cid:246)dziemy jej tutaj dok(cid:228)adnie oma- wia(cid:232). Warto wiedzie(cid:232), (cid:276)e powoduje zapisanie stanu przepisu kulinarne- go i wskazuje koniec procesu edycji. Ponadto jest powi(cid:241)zana z funkcj(cid:241) save() zdefiniowan(cid:241) w kontrolerze EditCtrl. (cid:120) Dyrektywa ng-model s(cid:228)u(cid:276)y do po(cid:228)(cid:241)czenia ró(cid:276)nych pól tekstowych for- mularza sieciowego z polami modelu. (cid:120) Jednym z najbardziej interesuj(cid:241)cych aspektów omawianej strony jest umieszczona w cz(cid:246)(cid:264)ci po(cid:264)wi(cid:246)conej li(cid:264)cie sk(cid:228)adników dyrektywa ng- controller, której naprawd(cid:246) warto po(cid:264)wi(cid:246)ci(cid:232) nieco uwagi i spróbowa(cid:232) w pe(cid:228)ni zrozumie(cid:232) sposób jej dzia(cid:228)ania. Zobaczmy wi(cid:246)c, co si(cid:246) tutaj dzieje. Lista sk(cid:228)adników jest wy(cid:264)wietlana, a zawieraj(cid:241)cy j(cid:241) znacznik jest po- wi(cid:241)zany z dyrektyw(cid:241) ng-controller. Oznacza to, (cid:276)e ca(cid:228)y znacznik ul znajduje si(cid:246) w zasi(cid:246)gu kontrolera IngredientsCtrl. Móg(cid:228)by(cid:264) w tym miejscu zapyta(cid:232): co z rzeczywistym kontrolerem EditCtrl powi(cid:241)zanym z szablonem? Jak si(cid:246) okazuje, IngredientsCtrl jest tworzony jako kon- troler potomny EditCtrl i tym samym dziedziczy po nim. Dlatego te(cid:276) dost(cid:246)p do obiektu recipe nast(cid:246)puje z poziomu kontrolera EditCtrl. Ponadto kontroler IngredientsCtrl dodaje metod(cid:246) addIngredient() u(cid:276)y- wan(cid:241) przez dyrektyw(cid:246) ng-click i dost(cid:246)pn(cid:241) jedynie w zasi(cid:246)gu znacz- nika ul . Dlaczego zdecydowali(cid:264)my si(cid:246) na takie rozwi(cid:241)zanie? To naj- lepszy sposób na rozdzielenie obowi(cid:241)zków. Po co umieszcza(cid:232) metod(cid:246) addIngredient() w kontrolerze EditCtrl, skoro 99 szablonu jej nie po- trzebuje? Kontrolery potomne i zagnie(cid:276)d(cid:276)one doskonale sprawdzaj(cid:241) si(cid:246) w tego rodzaju sytuacjach i pozwalaj(cid:241) na oddzielenie logiki biznesowej przez umieszczenie jej w (cid:228)atwiejszych do zarz(cid:241)dzania elementach. 120 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę (cid:120) Pozosta(cid:228)e dyrektywy, które chcemy tutaj omówi(cid:232), s(cid:241) kontrolkami prze- znaczonymi do weryfikacji formularza sieciowego. We frameworku AngularJS mo(cid:276)na bardzo (cid:228)atwo okre(cid:264)li(cid:232), (cid:276)e dane pole formularza jest wymagane. W tym celu wystarczy doda(cid:232) do tego pola dyrektyw(cid:246) required (jak to zrobiono w omawianym fragmencie kodu). Rodzi si(cid:246) jednak pytanie: co dalej? Przechodzimy do przycisku Zapisz. Zwró(cid:232) uwag(cid:246) na u(cid:276)ycie dyrekty- wy ng-disabled, która ma warto(cid:264)(cid:232) recipeForm.$invalid. Cz(cid:228)on pierwszy (recipeForm) to nazwa formularza zawieraj(cid:241)cego deklaracj(cid:246) dyrektywy. Framework AngularJS dodaje do niego pewne zmienne specjalne (za- liczamy do nich $valid i $invalid) pozwalaj(cid:241)ce na kontrolowanie ele- mentów formularza sieciowego. AngularJS wyszukuje wszystkie wy- magane elementy, a nast(cid:246)pnie odpowiednio uaktualnia wspomniane zmienne specjalne. Je(cid:276)eli pole s(cid:228)u(cid:276)(cid:241)ce do podania nazwy przepisu kuli- narnego pozostanie niewype(cid:228)nione, warto(cid:264)ci(cid:241) recipeForm.$invalid b(cid:246)dzie true (a warto(cid:264)ci(cid:241) $valid b(cid:246)dzie false) i przycisk Zapisz zostanie zablo- kowany. Istnieje równie(cid:276) mo(cid:276)liwo(cid:264)(cid:232) okre(cid:264)lenia minimalnej i maksymalnej d(cid:228)ugo(cid:264)ci pola tekstowego, a tak(cid:276)e wzorzec wyra(cid:276)enia regularnego przeznaczonego do przeprowadzenia weryfikacji danego pola. Co wi(cid:246)cej, pewne funkcje zaawansowane mo(cid:276)na wykorzysta(cid:232) do wy(cid:264)wietlania komunikatów b(cid:228)(cid:246)dów po wyst(cid:241)pieniu pewnych okre(cid:264)lonych warunków. Spójrzmy na prosty przyk(cid:228)ad: form name= myForm Nazwa u(cid:285)ytkownika: input type= text name= userName ng-model= user.name ng-minlength= 3 span class= error ng-show= myForm.userName.$error.minlength Zbyt krótka! /span /form Za pomoc(cid:241) u(cid:276)ycia dyrektywy ng-minlength w powy(cid:276)szym fragmencie ko- du zdefiniowano, (cid:276)e nazwa u(cid:276)ytkownika musi sk(cid:228)ada(cid:232) si(cid:246) z przynajmniej trzech znaków. Teraz formularz zostaje wype(cid:228)niony danymi pochodz(cid:241)cymi z obiektu scope — w omawianym przyk(cid:228)adzie to jedynie userName. Wszystkie pola tekstowe maj(cid:241) obiekt $error (zawiera informacje o rodzaju ewentual- nego b(cid:228)(cid:246)du: required, minlength, maxlength lub pattern) oraz w(cid:228)a(cid:264)ciwo(cid:264)(cid:232) $valid wskazuj(cid:241)c(cid:241) poprawno(cid:264)(cid:232) b(cid:241)d(cid:274) te(cid:276) niepoprawno(cid:264)(cid:232) danych wej(cid:264)ciowych. Kontrolery, dyrektywy i us(cid:293)ugi (cid:95) 121 Kup książkęPoleć książkę Takie rozwi(cid:241)zanie pozwala na selektywne wy(cid:264)wietlanie u(cid:276)ytkownikowi komunikatu b(cid:228)(cid:246)du w zale(cid:276)no(cid:264)ci od jego rodzaju, jak to pokazano w powy(cid:276)- szym fragmencie kodu. Do drugiego przycisku do(cid:228)(cid:241)czona jest dyrektywa ng-click u(cid:276)ywana pod- czas usuwania przepisu kulinarnego. Zwró(cid:232) uwag(cid:246), (cid:276)e przycisk jest wy- (cid:264)wietlany tylko wtedy, gdy przepis nie zosta(cid:228) jeszcze zapisany. Wprawdzie znacznie sensowniejsze wydaje si(cid:246) u(cid:276)ycie ng-hide= recipe.id , ale czasami bardziej semantyczne rozwi(cid:241)zanie to ng-show= !recipe.id . Oznacza to wy- (cid:264)wietlenie przycisku, gdy przepis kulinarny nie zawiera identyfikatora, zamiast ukrywania przycisku, je(cid:264)li przepis ma zdefiniowany identyfikator. Testy Wstrzymywali(cid:264)my si(cid:246) z przedstawieniem testów wraz z kontrolerami, ale musia(cid:228)e(cid:264) si(cid:246) spodziewa(cid:232), (cid:276)e kiedy(cid:264) wreszcie do nich przejdziemy. W tym podrozdziale zaprezentowane zostan(cid:241) testy, które nale(cid:276)y utworzy(cid:232) dla przygotowanego dot(cid:241)d fragmentu kodu. Dowiesz si(cid:246) równie(cid:276), jak tworzy si(cid:246) takie testy. Testy jednostkowe Najwa(cid:276)niejszy rodzaj testów to testy jednostkowe. Pozwalaj(cid:241) one na spraw- dzenie, czy opracowane kontrolery (dyrektywy i us(cid:228)ugi) maj(cid:241) prawid(cid:228)ow(cid:241) struktur(cid:246) i konstrukcj(cid:246) oraz czy dzia(cid:228)aj(cid:241) zgodnie z oczekiwaniami. Zanim przejdziemy do poszczególnych testów jednostkowych, warto spoj- rze(cid:232) na szkielet przeznaczony dla wszystkich testów jednostkowych doty- cz(cid:241)cych kontrolera: describe( Kontrolery , function() { var $scope, ctrl; // W te(cid:286)cie nale(cid:298)y wskaza(cid:252) modu(cid:225). beforeEach(module( guthub )); beforeEach(function() { this.addMatchers({ toEqualData: function(expected) { return angular.equals(this.actual, expected); } }); }); describe( ListCtrl , function() {....}); // Miejsce na opisanie pozosta(cid:225)ych kontrolerów. }); 122 (cid:95) Rozdzia(cid:293) 4. Analiza aplikacji AngularJS Kup książkęPoleć książkę Przygotowany szkielet (tutaj nadal wykorzystujemy styl Jasmine do tworze- nia testów) wykonuje kilka zada(cid:254). 1. Tworzy globalnie (przynajmniej dla testu) dost(cid:246)pny obiekt scope i kon- troler, a wi(cid:246)c nie trzeba si(cid:246) przejmowa(cid:232) tworzeniem nowej zmiennej dla ka(cid:276)dego kontrolera. 2. Inicjalizuje modu(cid:228) u(cid:276)ywany przez aplikacj(cid:246) (w omawianym przyk(cid:228)adzie jest to GutHub). 3. Dodaje specjalne dopasowanie nazywane equalData. Pozwala ono na prze- prowadzanie asercji na obiektach (cid:274)ród(cid:228)a (na przyk(cid:228)ad przepisach kuli- narnych) zwracanych przez us(cid:228)ug(cid:246) $resource lub na wywo(cid:228)anie RESTful. Pami(cid:246)taj o konieczno(cid:264)ci dodania specjalnego dopasowania na- zywanego equalData za ka(cid:276)dym razem, gdy zachodzi potrzeba stosowania asercji na zwróconych obiektach ngResource. Wi(cid:241)(cid:276)e si(cid:246) to z faktem, (cid:276)e zwrócone obiekty ngResource maj(cid:241) metody do- datkowe, których zwyk(cid:228)e wykonanie zako(cid:254)czy si(cid:246) niepowodze- niem, poniewa(cid:276) oczekiwane s(cid:241) wywo(cid:228)ania equalData. Maj(cid:241)c przygotowany szkielet, spójrzmy na gotowy test jednostkowy prze- znaczony dla kontrolera ListCtrl: describe( ListCtrl , function() { var mockBackend, recipe; // _$httpBackend_ to nazwa taka sama jak $httpBackend. Zastosowany zapis s(cid:225)u(cid:298)y do odró(cid:298)nienia // zmiennych wstrzykni(cid:266)tych od zmiennych lokalnych. beforeEach(inject(function($rootScope, $controller, _$httpBackend_, Recipe) { recipe = Recipe; mockBackend = _$httpBackend_; $scope = $rootScope.$new(); ctrl = $controller( ListCtrl , { $scope: $scope, recipes: [1, 2, 3] }); })); it( Wynikiem powinna by(cid:202) lista przepisów kulinarnych , function() { expect($scope.recipes).toEqual([1, 2, 3]); }); }); Jak zapewne pami(cid:246)tasz, kontroler ListCtrl nale(cid:276)y do najprostszych w apli- kacji. Konstruktor kontrolera pobiera po prostu list(cid:246) przepisów, a nast(cid:246)p- nie zapisuje je w obiekcie. Wprawdzie mo(cid:276)na do tego utworzy(cid:232) test, ale wydaje si(cid:246) to zb(cid:246)dne. W omawianym przyk(cid:228)adzie mimo wszystko utwo- rzyli(cid:264)my test, poniewa(cid:276) testy jednostkowe s(cid:241) wspania(cid:228)e! Testy (cid:95) 123 Kup książkęPoleć książkę Znacznie ciekawiej robi si(cid:246) w przypadku us(cid:228)ugi MultiRecipeLoader. Wy- mieniona us(cid:228)uga jest odpowiedzialna za pobranie listy przepisów kulinar- nych z serwera i przekazanie ich jako argumentu (kiedy zastosowana jest prawid(cid:228)owa konfiguracja za pomoc(cid:241) us(cid:228)ugi $route): describe( MultiRecipeLoader , function() { var mockBackend, recipe, loader; // _$httpBackend_ to nazwa taka sama jak $httpBackend. Zastosowany zapis s(cid:225)u(cid:298)y do odró(cid:298)nienia // zmiennych wstrzykni(cid:266)tych od zmiennych lokalnych. beforeEach(inject(function(_$httpBackend_, Recipe, MultiRecipeLoader) { recipe = Recipe; mockBackend = _$httpBackend_; loader = MultiRecipeLoader; })); it( Wynikiem powinno by(cid:202) wczytanie listy przepisów kulinarnych , function() { mockBackend.expectGET( /recipes ).respond([{id: 1}, {id: 2}]); var recipes; var promise = loader(); promise.then(function(rec) { recipes = rec; }); expect(recipes).toBeUndefined(); mockBackend.flush(); expect(recipes).toEqualData([{id: 1}, {id: 2}]); }); }); // Miejsce na opisanie pozosta(cid:225)ych kontrolerów. Test us(cid:228)ugi MultiRecipeLoader odbywa si(cid:246) przez przygotowanie us(cid:228)ugi Http (cid:180)Backend w naszym te(cid:264)cie. Obiekt pochodzi z pliku angular-mocks.js i jest do(cid:228)(cid:241)czany w trakcie przeprowadzania testów. Po prostu wstrzykni(cid:246)cie go do metody beforeEach() jest wystarczaj(cid:241)ce, aby mo(cid:276)na by(cid:228)o konfigurowa(cid:232) oczekiwania. W drugim, znacznie ciekawszym te(cid:264)cie oczekiwanie zosta(cid:228)o zdefiniowane jako wywo(cid:228)anie server GET do recipes, a wynikiem powinna by(cid:232) tablica obiektów. Nast(cid:246)pnie u(cid:276)ywamy dopasowania w celu spraw- dzenia, czy uzyskany wynik jest dok(cid:228)adnie zgodny z oczekiwaniami. Zwró(cid:232) uwag(cid:246) na wywo(cid:228)anie flush() w obiekcie makiety, przekazuj(cid:241)ce odpowied(
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

AngularJS
Autor:
,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: