, do którego wstawiana będzie treść za po m ocą dyrektywy ng-tran sclu d e. Jest to kontener na treść H TM L dyrektywy (wszystkie indy widualne karty), która zostanie do niego wstawiona po uruchom ieniu aplikacji. • N astępnie zam iast fu nk cji l i nk d efiniujem y kontroler dyrektywy. R obim y tak, bo chcem y, aby dyrektywy potom ne dyrektywy tab s m iały dostęp do funkcjonalności tej dyrektywy. D e finicja kontrolera dyrektywy jest dobrym rozwiązaniem zawsze wtedy, gdy trzeba umożliwić kom unikację między dyrektywą nadrzędną i dyrektywami podrzędnymi lub między dyrek tywami równorzędnymi. • Kontroler dyrektywy jest funkcją pobierającą zakres i element. Funkcja ta jest podobna do funkcji li nk, ale różni się od niej tym, że funkcje zdefiniowane w kontrolerze dla th is są do stępne dla kontrolerów potom nych i równorzędnych (niebawem dowiesz się, w jak i sposób). Zatem c o n t r o l l e r m oże definiow ać fu nkcje specyficzne dla egzem plarza dyrektywy przez zdefiniowanie ich dla $scope, ja k to robiliśm y do tej pory, oraz może udostępniać interfejs API złożony z funkcji i zm iennych zdefiniowanych dla t hi s , czyli egzemplarza kontrolera. • W tym przypadku na obiekcie scope zdefiniowaliśmy zm ienną tab s i funkcje sele ctT a b oraz isS e le cte d , wykorzystywane przez szablon dyrektywy do wybierania i wyróżniania wybranych kart. Obie te funkcje ustawiają i kasują wartość zm iennej s e le c te d dla indywidualnej karty i w ten sposób sterują wyświetlaniem i ukrywaniem kart. • Ponadto na egzemplarzu kontrolera zdefiniowaliśmy funkcję o nazwie re g iste rT a b . Jako że funkcja ta nie jest zdefiniowana na zakresie, nie jest ona dostępna z kodu H TM L dyrektywy. Dodaje ona tytuł i zakres do tablicy, która jest stosowana do wyświetlania listy kart.
Kontrolery dyrektyw i funkcja require
| 225
Teraz przyjrzymy się dyrektywie t a b , która podczepia się do dyrektywy t a b s i wykorzystuje zde finiowany przez nas kontroler: // Plik: r13/directive-controllers/tab.js a n g u la r . m o d u le ( 's t o c k M a r k e t A p p ') . d i r e c t i v e ( 't a b ', [ f u n c t io n ( ) { retu rn { r e s t r ic t : 'E ', t ra n s c lu d e : tru e , tem p late: '< d i v n g - s h o w = "s e le c t e d " n g - t r a n s c lu d e > < / d iv > ', r e q u ir e : ' ' t a b s ' , scope: tru e , l i n k : fu n c t io n ( $ s c o p e , $elem ent, $ a t t r , t a b C t r l) t a b C t r l. r e g i s t e r T a b ( $ a t t r . t i t l e , $ s c o p e ) ;
{
} }; }]);
Dyrektywa t a b jest znacznie prostsza: 1. Pierwszą czynnością dyrektywy t a b jest zdefiniow anie transkluzji, ponieważ zawiera własną definicję szablonu. 2. W szablonie ( t e m p la t e ) za pom ocą dyrektywy n g - t r a n s c l u d e wstawiliśmy treść do elementu < d iv > ,
a także dodaliśmy do niego warunek pozwalający selektywnie ukrywać i pokazywać go
w zależności od wartości zm iennej s e l e c t e d z zakresu. 3. D odaliśm y nowy klucz o nazwie r e q u i r e i wartości '' t a b s (znaczenie symbolu ' wyjaśniliśmy w punkcie „O pcje klucza require”). Klucz ten inform uje system AngularJS, że aby dyrektywa tab
działała, jed en z je j elementów nadrzędnych w kodzie H TM L m usi być dyrektywą t a b s ,
oraz że chcemy, by kontroler dyrektywy t a b s był dostępny dla dyrektywy t a b . 4. Zdefiniowaliśm y nowy zakres dla tej dyrektywy, dzięki czemu je j zm ienne lokalne nie będą kolidować z elem entam i zakresu nadrzędnego. 5. W funkcji l i n k wykorzystujemy kontroler przekazany do niej w czwartym argumencie (po za kresie, elemencie i atrybutach). Jest to egzemplarz kontrolera zdefiniowanego przez nas w dy rektywie t a g s , który system AngularJS wstrzykuje dynamicznie na podstawie tego, co znajdzie. 6. W funkcji l i n k rejestrujem y kartę za pom ocą funkcji zdefiniowanej w nadrzędnej dyrektywie tab s.
Teraz przepływ sterowania w aplikacji jest następujący: 1. Gdy w kodzie H TM L zostanie znaleziona dyrektywa t a b s (nadrzędna), następuje transkluzja treści i zarezerwowanie m iejsca dla kart w dokum encie (za pom ocą dyrektywy n g - r e p e a t ). Karty są wstawiane poniżej. 2. Każda karta jest rejestrow ana w rodzicu, dzięki czemu nadrzędny kontroler kart jest in fo r m ow any o tym , która karta je st aktualnie zaznaczona, i m oże ją wyróżnić oraz wyświetlić, a pozostałe karty ukryć. 3. Każda karta wykorzystuje transkluzję w odniesieniu do swojej treści, którą pakuje w kontener, aby m óc ją ukrywać i pokazywać w zależności od potrzeby.
226
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
4. Przy rejestracji kontroler dyrektywy tab s ustawia pierwszą kartę jako wybraną (przy użyciu zm iennej zakresu przekazanego do kontrolera). 5. Później dyrektywa n g - c l i c k obsługuje ukrywanie i wyświetlanie indywidualnych kart za po m ocą funkcji zdefiniowanych w zakresie dyrektywy tabs.
Opcje klucza require Słowo kluczowe req u ire w obiekcie definicji dyrektywy jako w artość może przyjąć łańcuch lub tablicę łańcuchów reprezentujących nazwy dyrektyw, które m uszą zostać użyte wraz z bieżącą dyrektywą. N a przykład: r e q u ir e :
't a b s '
Ten kod nakazuje systemowi AngularJS znalezienie dyrektywy tab s udostępniającej kontroler na tym samym elem encie, do którego została zastosowana bieżąca dyrektywa. Analogicznie: r e q u ir e :
[ 't a b s ',
'n g M o d e l']
T en kod inform uje A ngularJS, że w elem encie z bieżącą dyrektywą potrzebne są dyrektywy tab s i ng-model. Jeśli w r e qui r e zostanie przekazana tablica, fu n kcja l i nk w czwartym argum encie otrzyma tablicę kontrolerów, a nie tylko jed en kontroler. Każdy łań cu ch m oże m ieć pewne przed rostki określające sposób traktow ania przez system A ngularJS znalezionej dyrektywy. N a przykład: r e q u ir e :
'? t a b s '
Ten kod nakazuje systemowi AngularJS znalezienie dyrektywy tabs w tym samym elemencie i po woduje przekazanie wartości null w czwartym argumencie do funkcji l i nk, jeśli szukana dyrek tywa nie zostanie znaleziona. Innym i słowy, przedrostek ? oznacza zależność opcjonalną. Ewentualnie m ożna nakazać systemowi AngularJS, aby szukał dyrektywy wyżej w hierarchii. Służy do tego poniższy przykład: r e q u ir e :
'^ t a b s '
Jest to konstrukcja z poprzedniego przykładu, w którym poinform ow aliśm y system A ngularJS, że dyrektywa tab s musi być zadeklarowana w jednym z elem entów nadrzędnych (niekoniecznie m usi to być bezpośredni przodek). Przedrostków tych m ożna też używać w kom binacjach, np.: r e q u ir e :
'? ^ t a b s '
Ten kod oznacza, że elem ent nadrzędny naszej dyrektywy może, ale nie m usi m ieć zadeklarowa nej dyrektywy tab s, która w razie jeśli jest dostępna, powinna zostać wstrzyknięta do funkcji l i nk naszej dyrektywy.
Kontrolery dyrektyw i funkcja require
| 227
Dyrektywy wejściowe i ng-model W poprzednim podrozdziale pokazaliśmy, jak się tworzy kontrolery dyrektyw oraz jak się je wyko rzystuje do kom unikacji między dyrektywami i w celu wymiany inform acji o stanie. W tym punk cie zastosujem y zdobytą wiedzę, rozszerzymy standardową dyrektywę n g -m o d e l oraz dokonam y integracji z zewnętrznymi widżetami wejściowymi. Chodzi nam o to, że dyrektywa n g -m o d e l jest dobra w tym, co robi, czyli dwustronnym wiązaniu danych. Jeżeli do naszej aplikacji wprowadzi m y nowy widżet wejściowy, to chcem y, aby zachowywał się w taki sam sposób ja k ta dyrektywa, tzn. dodajem y go do kodu H TM L, deklarujem y dyrektywę n g -m o d e l dla niego i gotowe. Im p lem en tację opisanego rozw iązania przedstaw im y na przykładzie in teg ra cji z naszą apli k acją zewnętrznego suwaka. Będzie to zbudowany na bazie biblioteki jQ u ery widżet n o U i S l i d e r (http://refreshless.com /nouislider/), który zapakujemy do dyrektywy wielokrotnego użytku. Zacznie m y od pliku index.htm l, w którym zobaczysz, jak planujemy używać naszej dyrektywy:
< t i t l e > A p l i k a c j a z su w a k ie m < / title > < lin k r e l= "s t y le s h e e t " h r e f = "j q u e r y .n o u is lid e r . c s s "> < s t y l e t y p e = "t e x t / c s s "> . s lid e r { d is p la y : b lo c k ; h e ig h t : 20px; m a rgin : 20px; } < / s t y le > < d iv n g - c o n t r o ll e r = " M a in C t r l < d iv > A k t u a ln a w a rto ść suwaka:
as m a in C t r l" >
{ {m a in C t r l. s e le c t e d V a lu e } }
< n o - u i- s li der c l a s s = " s l id e r " n g - m o d e l= "m a in C tr l. s e le c t e d V a lu e " ra n g e -m in = "5 0 0 " ra n g e -m a x = "5 0 0 0 "> < / n o - u i- s lid e r > < d iv > < in p u t type ="n u m b e r" ng-m odel="m ai n C t r l . t e x t V a lu e " m in = "5 0 0 " m ax="5000" p la c e h o ld e r= "U s ta w w a r to ść "> Ustaw w a rto ść suwaka
228
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
< s c r i pt < s c r ip t < s c r i pt < s c r ip t < s c r ip t
s r c = " h t t p : / / c o d e . jq u e r y . c o m / j q u e r y - 1 . 1 1 . 1 . js "> < / s c r i p t > s r c = "j q u e r y . n o u i s l i d e r . m in . j s " > < / s c r i pt> s r c = " h t t p s :/ / a j a x . g o o g le a p is . c o m / a ja x / li b s / a n g u la r j s / 1 . 3 . 1 1 / a n g u l a r . j s " > < / s c r i pt> s r c = " a p p . j s " > < / s c r i pt> s r c = " n o u i- s li d e r . js "> < / s c r ip t >
Kod HTM L naszej aplikacji jest nieskomplikowany. W nagłówku dołączyliśmy plik CSS dla n o U iS l i d e r , a na końcu najpierw dołączyliśmy bibliotekę jQ uery, po niej plik jqu ery.n ou islider.m in .js, a następ nie bibliotekę AngularJS i standardowe zależności. W elem encie < d i v > kontrolującym aplikację najpierw wyświetlamy bieżącą w artość zm iennej s e l e c t e d V a l ue
z kontrolera, a później wiążemy dyrektywę n o - u i - s l i d e r z tą zm ienną za pom ocą
dyrektywy n g - m o d e l . D odatkow o zdefiniow aliśm y przedział d ozw olonych w artości suwaka za pom ocą atrybutów r a n g e - m in i r a n g e - m a x . Dalej znajdują się sekcja zawierająca pole tekstowe powiązane z inną zm ienną ( t e x t V a l u e ) i przy cisk. K liknięcie tego przycisku pow inno spow odow ać ustaw ienie suw aka n a w artość wpisaną w polu tekstowym. Teraz spójrzm y na kontroler tej aplikacji: / / Plik: r13/directive-slider/app.js a n g u la r . m o d u le ( 's li d e r A p p ', []) . c o n t r o l l e r ( 'M a i n C t r l ', [ f u n c t io n ( ) var s e lf = t h is ;
{
s e lf . s e l e c t e d V a l u e = 2000; s e l f . t e x t V a l u e = 4000; s e lf . s e t S e le c t e d V a lu e = f u n c t io n ( ) { s e lf . s e l e c t e d V a l u e = s e lf . t e x t V a l u e ; }; }]);
K o n troler tej aplikacji je s t niew ielki. Zaw iera tylko dwie zm ien ne m odelow e ( s e l e c t e d V a l u e i t e x t V a l u e ) oraz fu n k cję p o b ierającą w artość zm ien nej t e x t V a l ue i zapisującą ją w zm ien nej s e le c t e d V a lu e .
Celem tej funkcji jest pobranie wartości z pola tekstowego i ustawienie je j w su
waku. O bie zm ienne m ają ustaw ioną w artość początkow ą, dzięki czem u in terfejs użytkownika jest nieco urozmaicony. Teraz m ożem y w końcu przyjrzeć się dyrektywie n o U i S l i d e r : / / Plik: r13/directive-slider/noui-slider.js a n g u la r . m o d u le ( 's lid e r A p p ') . d i r e c t i v e ( 'n o U i S l i d e r ', [ f u n c t io n ( ) { retu rn { r e s t r ic t : 'E ', r e q u ir e : 'n g M o d e l', l i n k : f u n c t io n ( $ s c o p e , $elem ent, $ a t t r , n g M o d e lC trl)
{
$ e le m e n t .n o U iS lid e r ( {
/ / w kontrolerze ngModelCtrl m oże jeszcze nie być wartości początkow ej sta rt:
0,
Kontrolery dyrektyw i funkcja require
| 229
ran ge :
{
/ / $attr domyślnie dostarcza wartości łańcuchowych / / nouiSlider działa na liczbach, dlatego konieczna jest konwersja m in: N u m b e r($ a ttr.ra n g e M in ), max: N u m b e r($ attr.ran ge M ax) } });
// Gdy w AngularJS zm ienią się dane, należy poinform ow ać o tej zm ianie dyrektywę zewnętrzną. n g M o d e lC tr l.$ r e n d e r = f u n c t io n ( ) { $ e le m e n t .v a l(n g M o d e lC t r l.$ v i ew Value); };
// Jeśli dane zm ienią się p oza AngularJS, $ e le m e n t . o n ( 's e t ',
f u n c t io n ( a r g s )
{
/ / to należy poinform ow ać także AngularJS, że trzeba zaktualizować interfejs. $ s c o p e . $ a p p ly ( f u n c t io n ( )
{
/ / ustawienie danych w AngularJS n g M o d e lC t r l. $ s e t V ie w V a lu e ( $ e le m e n t.v a l( ) ) ; }); }); } }; }]);
Jak jest zbudowana ta dyrektywa? Przeanalizujem y ją krok po kroku: 1. Utworzyliśmy dyrektywę elementową wymagającą tego, aby w tym samym elem encie, w k tó rym zostanie użyta, była też zdefiniowana dyrektywa n g M o d e l . 2. W funkcji l i n k najpierw tworzymy n o U iS l i d e r przez wywołanie konstruktora z odpowiednimi parametrami. Wykorzystujemy atrybuty z kodu HTML, które konwertujemy z łańcuchów na liczby. 3. Jako że n o U i S l i d e r jest wtyczką do jQ uery, a w pliku in d ex .h tm l bibliotekę tę ładujem y przed AngularJS, bezpośrednio wywołujemy funkcję n o U i S l i d e r na naszym elem encie, ponieważ biblioteka jQ uery idealnie integruje się z AngularJS. 4. Następnie, aby sfinalizować integrację dyrektywy ngM o d e l z naszym zewnętrznym składnikiem, m usim y wykonać dwie czynności: a. Gdy nastąpi zm iana danych w AngularJS, m usim y zaktualizować zewnętrzny kom ponent interfejsu użytkownika. W tym celu przesłaniam y m etodę $ r e n d e r kontrolera n g M o d e lC t r l i ustawiamy w niej wartość w zewnętrznym kom ponencie. N ajnowsza wartość ustawiona w zmiennej, do której odwołuje się n g M o d e l , jest dostępna w kontrolerze ngM odel C t r l , w zmien nej $ v ie w V a l u e . System AngularJS wywołuje m etodę $ r e n d e r za każdym razem, gdy zmieni się w nim wartość m odelu (np. kiedy zostanie zainicjow ana w naszym kontrolerze). b. Gdy dane zm ienią się poza system em AngularJS, m usim y go o tej zmianie poinform ować. W tym celu wywołujemy funkcję $ s e t V ie w V a l u e na n g M o d e l C t r l z najnowszą wartością. 5. Ponadto, ja k wspom inaliśm y wcześniej, system AngularJS aktualizuje interfejs użytkownika zawsze, gdy dowie się o zmianach w kontrolerze. Zewnętrzny komponent interfejsu użytkownika nie podlega cyklowi życia AngularJS, więc musimy ręcznie wywoływać funkcję $ s c o p e . $ a p p l y ( ) , aby wymusić aktualizację. Funkcja ta przyjmuje jako argum ent opcjonalną funkcję i powo duje uruchom ienie cyklu obliczeniowego AngularJS odpowiedzialnego za aktualizację inter fejsu użytkownika o najnowsze wartości.
230
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
Podczas integrowania z aplikacją zewnętrznych kom ponentów interfejsu użytkownika umożli wiających wprowadzanie danych zawsze dobrym pomysłem jest wykorzystanie dyrektywy ngModel , dzięki czemu kom ponent ten będzie działał jak każdy inny widżet wejściowy. Jeśli skorzystasz z tej rady, musisz też zająć się dwustronnym wiązaniem danych: • Gdy dane w systemie AngularJS ulegną zm ianie, m usim y odpowiednio zaktualizować k om ponent zewnętrzny (przez przesłonięcie funkcji ngM odelCtrl.$render). • Kiedy dane zm ienią się poza system em AngularJS (przez zdarzenie spoza tego systemu), n a leży tę zm ianę przechw ycić i na je j podstawie zaktualizow ać m odel AngularJS (wywołując funkcję ngModelCtrl.$setViewValue z zaktualizowaną wartością). Jako że procedury nasłuchowe są dodawane tylko do elem entu, usunięcie tego elementu oznacza także skasowanie tych procedur. Jeśli dodamy kolejną procedurę nasłuchową, to nią również m u sim y się później zająć.
Tworzenie walidatorów W iesz już, do czego służą kontrolery dyrektyw i ja k przy użyciu dyrektywy ngModel tworzyć wła sne dyrektywy wejściowe, więc możesz przystąpić do tworzenia własnego walidatora. Jak pokaza liśmy w rozdziale 2., system AngularJS zawiera wiele wbudowanych dyrektyw do sprawdzania po prawności danych wprowadzonych w form ularzach, np. required, ng-required, ng-minlength itd. Jest to solidny zestaw narzędzi, których m ożna używać w projektach. Ale czasami pewne konkretne czynności weryfikacyjne wykonuje się wiele razy w różnych sytu acjach. W takim przypadku lepiej jest utworzyć własne walidatory, niż stosować standardowe walidatory AngularJS. Przeanalizujem y trochę wydumany przykład aplikacji, w której chcem y sprawdzić, czy użytkow nik wpisał w polu tekstowym prawidłowy kod pocztowy w form acie amerykańskim. W USA kod pocztowy występuje w jednym z trzech następujących formatów: • 12345, • 12345 1234, • 12345-1234. N ajpierw zobaczm y plik in d ex .h tm l, dem onstrujący sposób użycia naszej dyrektywy walidacyjnej:
< t i t l e > A p l i k a c j a g ie id o w a < / t it le > < sty le > i n p u t . n g - i n v a l id { b a c k g ro u n d : p in k ; } < / s t y le > < d iv n g - c o n t r o ll e r = " M a in C t r l
as m a in C t r l" >
Kontrolery dyrektyw i funkcja require
| 231
W pisyw anie kodu pocztowego
Kod pocztow y może być w p isa n y w jednym z t rz e c h form atów :
< u l> < l i > 1 2 3 4 5 < / li > < l i >12345 1 2 3 4 < / li > < l i > 1 2 3 4 5 -1 2 3 4 < / li> < / u l> < s c r ip t s r c = " h t t p s : / / a j a x . g o o g le a p i s .c o m / a ja x / li b s / a n g u la r j s / 1 . 3 . 1 1 / a n g u la r . j s " > < / s c r ip t > < s c r i pt s r c = " a p p . j s " > < / s c r i pt> < s c r i pt s r c = " d i r e c t i v e . j s " > < / s c r i pt>
W kodzie tym interesuje nas tylko form ularz zawierający pole wejściowe z dyrektywą n g - m o d e l . C hcem y m ieć pew ność, że użytkownik popraw nie wpisze kod pocztowy, i dlatego do pola tego dodaliśmy dyrektywę walidacyjną v a l i d - z i p . Zaraz za tym polem zdefiniowaliśmy elem ent < d i v > zawierający inform ację o błędzie, która zostanie wyświetlona, gdy użytkownik wpisze niepraw i dłowy tekst. Zawartość pliku ap p .js jest banalnie prosta i wygląda tak: // Plik: r13/directive-custom-validator/app.js angul a r . m o d u le ( 's t o c k M a r k e t A p p ',
[])
. c o n t r o l l e r ( 'M a i n C t r l ', [ f u n c t io n ( ) t h is .z ip = '';
{
}]);
Jest to definicja kontrolera, w którym pole z i p konstruktora zostało domyślnie ustawione na war tość pustą. Na koniec spójrzmy jeszcze na dyrektywę reprezentującą nasz walidator: // Plik: r13/directive-custom-validator/directive.js a n g u la r . m o d u le ( 's t o c k M a r k e t A p p ') . d i r e c t i v e ( 'v a l i d Z i p ', [ f u n c t io n ( ) { v a r zipC odeR egex = / ^ \ d { 5 ] ( ? : [ - \ s ] \ d { 4 ) ) ? $ / g ; retu rn { r e s t r i c t : 'A ', r e q u ir e : 'n g M o d e l', l i n k : f u n c t io n ( $ s c o p e , $elem ent, $ a t t r s , n g M o d e lC trl) n g M o d e lC t r l. $ v a lid a t o r s . z ip = f u n c t io n ( v a lu e ) { r e t u r n z ip C o d e R e g e x . t e s t ( v a lu e ) ; }; } }; }]);
232
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
{
Dyrektywa validZ ip definiuje wyrażenie regularne służące do weryfikacji, czy dany łańcuch re prezentuje prawidłowy kod pocztowy w formacie używanym w USA. W alidator ten zależy od kon trolera dyrektywy ngModel, który dołącza w swojej definicji. Na listingu widoczna jest też funkcja lin k , która zawiera logikę naszej dyrektywy. W systemie AngularJS 1.3 ułatwiono dodawanie własnych walidatorów do łańcucha walidatorów AngularJS. K ontroler ngM odelController udostępnia klucz $ v a lid a to rs . Każda dodana do niego funkcja zostanie wykonana przez AngularJS w celu sprawdzenia poprawności określonego pola, do którego została dodana dyrektywa walidacyjna. W tym przypadku do wspomnianego k o n tro lera dodaliśmy walidator zip, który pobiera bieżącą wartość z interfejsu użytkownika i zwraca wartość tru e, jeśli stwierdzi, że jest ona poprawna według wyrażenia regularnego, lub fa ls e w prze ciwnym wypadku. W efekcie klucz fo rm F ie ld .$ e rro r.z ip zostanie ustawiony na tru e albo fa ls e . W arto też zauważyć, że nazwa dyrektywy nie m a nic wspólnego z ostatecznym kluczem w obiek cie $e rro r. W obiekcie tym używany jest klucz zastosowany do dodania dyrektywy do listy walidatorów $ v a lid a to rs. Gdybyśmy chcieli wykonywać walidację asynchronicznie (np. sprawdzać dostępność nazwy użyt kow nika), to moglibyśm y dodać nasz walidator do klucza $asyncV alidators kontrolera ngModel ^ C o n tro ll er. Ale w przypadku walidacji asynchronicznej zamiast logicznej wartości tru e lub fa ls e należy zwrócić obietnicę AngularJS. Jeśli obietnica zostanie spełniona, pole zostaje uznane za prawidłowo wypełnione, a w przeciwnym razie następuje wyświetlenie inform acji o błędzie. N a leży też wiedzieć, że walidatory asynchroniczne są wywoływane dopiero po wszystkich walidatorach synchronicznych dla danego pola i pod warunkiem , że wszystkie one zwrócą wartość tru e. Aby m ożna było wyświetlić jakiś wskaźnik ładowania, podczas gdy wykonywana jest asynchro niczna operacja walidacji, kontrolka form ularza udostępnia zm ienną $pending. Gdyby więc nasza dyrektywa v alid Z ip przeprow adzała w alidację asy n ch ro n iczn ie, to m oglibyśm y spraw dzać zm ienną zipForm .zipField.$pending — jeśli m iałaby ona wartość tru e, m oglibyśmy wyświetlać obracającą się ikonkę, by poinform ow ać użytkownika, że cały czas coś się dzieje. Krótko mówiąc, wachlarz zastosowań walidatorów ogranicza tylko Tw oja wyobraźnia.
Kompilacja W swoim cyklu życia dyrektywa przechodzi przez dwie fazy: kom pilacji (compile) i łączenia (lin k ). Etap łączenia ju ż zgłębiliśmy szczegółowo, więc czas na dokładniejsze poznanie kroku kom pilacji. Zanim system dojdzie do etapu lin k dyrektywy, je j kod H TM L jest ju ż przetworzony i wszystkie potrzebne dyrektywy są pobrane przez kompilator AngularJS oraz powiązane z bieżącym zakresem. Jeżeli w tym m om encie dynamicznie dodamy do kodu H TM L jakąś dyrektywę albo dokonamy poważnej ingerencji w strukturę D O M , w ram ach której nastąpi integracja z istniejącym i dyrek tywami AngularJS, to dyrektywy te nie zadziałają prawidłowo. O d pow iednim m iejscem do szperania w kodzie H T M L szablonu i p rzekształcania struktury D O M jest krok compile w dyrektywie. N igdy n ie u żyw a się fu n k c ji lin k i compile razem , ponieważ jeśli użyje się klucza compil e, należy zw rócić z niego fu nkcję łączącą. Zobaczm y, ja k to wygląda w praktyce, na przykładzie dyrektywy o nazwie form-element:
Kompilacja
| 233
< t i t l e > A p l i k a c j a z dynamicznym fo rm u la rz e m < / ti t le > < d iv n g - c o n t r o ll e r = " M a in C t r l as m a in C t r l" > Nazwa u żytk o w n ika:
{{m a in C tr l.u se r n a m e }}
H a sło : { {m a in C t r l. p a s s w o r d } } < b u tt o n > W y ślij< / b u t to n > < s c r i pt s r c = " h t t p s :/ / a j a x . g o o g le a p i s . co m /a jax /li b s / a n g u la r j s / 1 . 3 . 1 1 / a n g u l a r . j s " > < / s c r i pt> < s c r i p t s r c = " a p p . j s " > < / s c r i pt> < s c r i p t s r c = " d i r e c t i v e . j s " > < / s c r i pt>
W kodzie tym zdefiniowaliśmy form ularz zawierający kilka etykiet i pól wejściowych. Każde pole ma własną logikę walidacji i własną wiadomość o błędzie. Zamiast pisać skomplikowane struktury H TM L przy użyciu technik opisanych w rozdziale 4., wykorzystamy dyrektywę f o r m - e l em ent , w której zdefiniujemy reguły walidacji i wiązania danych. Dyrektywa ta umożliwia również definiowanie po wiadomień o błędach, które m ożna automatycznie wyświetlać w odpowiednich sytuacjach, zależ nie od warunków. Jedynym wymogiem tej dyrektywy jest to, że m usi być używana w formularzu.
234
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
O to lista składników obsługiwanych przez naszą dyrektywę form-element: • pola wejściowe różnego typu (te x t, password itd.); • nazwa pola wejściowego, aby m ożna było znaleźć błędy; • zm ienna ngModel do związania; • etykieta do pokazania obok pola formularza; • każdy m echanizm walidacji oparty na ngModel, np. required, ngMinlength, ngPattern itd. K ontroler jest bardzo prosty: / / Plik: r13/directive-compile/app.js a n g u la r .m o d u le ('d y n a m ic F o rm A p p ', [ ]) . c o n t r o l l e r ( 'M a i n C t r l ', [ f u n c t io n ( ) var s e lf = t h is ;
{
s e lf.u se r n a m e = ' ' ; s e lf . p a s s w o r d = ' ' ; }]);
Kontroler ten definiuje tylko kilka zm iennych do wiązania w kodzie H TM L. Nie im plem entujem y funkcji onClick dla formularza, ponieważ nie interesują nas takie zdarzenia. Zadaniem naszej dyrektywy jest przeanalizowanie kodu H TM L i wykonanie następujących czynności: • wygenerowanie odpowiedniego znacznika wejściowego z odpowiednią dyrektywą ng-model i regułam i w alidacji; • wygenerowanie szablonu dla wszystkich wiadomości o błędach i zadbanie o to, by były wy świetlane w odpowiednich sytuacjach; • zignorowanie wszystkich atrybutów, które są nieznane lub nieobsługiwane przez dyrektywę; • dodanie do zakresu funkcji wyświetlających wiadomości o błędach. Zobaczm y teraz, ja k wygląda kod źródłowy naszej dyrektywy: / / Plik: r13/directive-compile/directive.js a n g u la r.m o d u le ('d y n a m ic F o rm A p p ') . d ir e c t iv e ( 'f o r m E le m e n t ', [ f u n c t io n ( )
{
retu rn { r e s t r ic t : 'E ', r e q u ir e : '^ f o r m ', sco pe : tru e , co m p ile : fu n c t io n ($ e le m e n t, $ a t t r s ) v a r e x p e c t e d In p u t A t t rs = { 'r e q u i r e d ': 'r e q u i r e d ', 'n g - m in le n g t h ': 'n g M in l e n g t h ', 'n g - p a t t e r n ': 'n g P a t t e r n '
{
// dalsze instrukcje };
/ / rozpoczęcie pobieran ia treści z kodu HTML v a r v a lid a t io n K e y s = $ e l e m e n t . f i n d ( 'v a l i d a t i o n ') ; v a r p r e s e n t V a lid a t io n K e y s = { } ; v a r inputName = $ a ttrs.n a m e ;
Kompilacja
| 235
a n g u la r . f o r E a c h ( v a lid a t io n K e y s , f u n c t io n ( v a lid a t io n K e y ) v a lid a t io n K e y = a n g u la r . e le m e n t ( v a lid a t io n K e y ) ; p r e s e n t V a li d a t io n K e y s [ v a li d a t i o n K e y . a t t r ( 'k e y ')] = v a l i d a ti o n K e y . t e x t ( ) ;
{
});
// rozpoczęcie generow ania finalnego elementu HTML v a r elementHtml = '< d i v > ' + '< l a b e l > ' + $ a t t r s . l a b e l + '< / l a b e l > '; elementHtml += '< in p u t t y p e = " ' + $ a t t r s . t y p e + ' " nam e="' + inputName + ' " n g -m o d e l= "' + $ a t t r s . b in d T o + ' " ' ; $ e le m e n t .r e m o v e A t t r ( 't y p e ') ; $ e le m e n t.re m o v e A ttr('n a m e ') ; f o r (v a r i in e x p e c te d ln p u t A tt rs ) { i f ( $ a t t r s [ e x p e c t e d In p u t A t t r s [ i ] ] !== u n d e fin e d ) elementHtml += ' ' + i + ' = " ' + $ a t t r s [ e x p e c t e d In p u t A t t r s [ i] ] + ' " ' ;
{
} $ e le m e n t .r e m o v e A t t r ( i) ; } elementHtml += ' > ' ; elementHtml '< sp a n ' '
+= n g - r e p e a t = "( k e y , te x t) in v a l i d a t o r s " n g -sh o w = "h a sE rro r(k e y )"' + n g - b i n d = "t e x t " > < / s p a n > ';
' +
elementHtml += '< / d i v > '; $ e l e m e n t.h tm l(elem entH tm l); r e t u r n f u n c t io n ( $ s c o p e , $elem ent, $ a t t r s , fo rm C t rl) { $ s c o p e . v a lid a t o r s = a n g u la r . c o p y ( p r e s e n t V a lid a t io n K e y s ) ; $ s c o p e . h a s E r r o r = f u n c t io n ( k e y ) { r e t u r n !! fo rm C trl [in p u t N a m e ][' $ e r r o r '] [k e y ]; }; }; } }; }]);
Oto najważniejsze cechy dyrektywy fo r m E le m e n t , której definicja znajduje się powyżej: 1. D odaliśm y kucz r e q u i r e , oznaczający, że dyrektywa m a być używana wewnątrz formularzy, oraz nadaliśm y je j nowy zakres podrzędny, aby dodane do niej funkcje nie nadpisały żadnych globalnych zm iennych lub funkcji. 2. Zdefiniowaliśm y też funkcję c o m p i le , wywoływaną z elem entem i atrybutami. Funkcja ta jest wykonywana, zanim zakres staje się dostępny, więc nie wstrzykujemy go do niej. 3. Zaczynamy pobierać i przetwarzać istniejący znacznik fo r m - e le m e n t z kodu H TM L oraz po bieram y reguły walidacji, wiadomości i istniejące atrybuty, które nas interesują. 4. Później rozpoczynam y generowanie nowego kodu H TM L dla dyrektywy. Jako że będziemy dynamicznie dodawać dyrektywy AngularJS, robim y to na etapie kom pilacji. Gdybyśmy za m iast tego zrobili to na etapie łączenia, system AngularJS nie wykryłby tych dyrektyw i apli kacja by nie działała.
236
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
5. Dodajem y znacznik wejściowy o nazwie ng-model i wszystkie reguły walidacji, które znaleźliśmy w kodzie H TM L. 6. Następnie zastępujem y istniejącą treść dyrektywy nowo wygenerowaną treścią. 7. Na koniec zwracamy funkcję postLink (nie m ożem y użyć słowa kluczowego lin k razem z compile, więc musimy zwrócić funkcję łączącą z etapu kompilacji), która dodaje tablicę val idators i funk cję hasError, służące do wyświetlania wiadomości o błędach w odpowiednich m om entach. Użyliśm y do tego kontrolera form, który został dołączony przez dyrektywę zgodnie ze stan dardami opisanymi w rozdziale 4.
Funkcje pomocnicze AngularJS Pewnie zauważyłeś, że w funkcji compile dyrektywy formElement wywołaliśmy funkcję o nazwie angular.forEach. System AngularJS udostępnia kilka globalnych funkcji pomocniczych pozwa lających wykonywać czynności, których nie da się wygodnie wykonać przy użyciu zwykłego in terfejsu API JavaScriptu. Oto lista niektórych z tych funkcji: angular.forEach Iterator do przeglądania obiektów i tablic pomocny przy pisaniu kodu w stylu funkcyjnym. angular.fromJson i angular.toJson Metody pomocnicze służące do konwersji łańcuchów na obiekty JSON i odwrotnie. angular.copy Funkcja wykonująca głęboką kopię danego obiektu i zwracająca nowo utworzony obiekt. angular.equals Funkcja sprawdzająca, czy dwa obiekty, wyrażenia regularne albo dwie tablice lub wartości są równe. Dla obiektów i tablic wykonuje porównywanie głębokie. an gu lar.isO bject, angular.isArray i angular.isFunction Metody pomocnicze do szybkiego sprawdzania, czy zmienna jest obiektem, tablicą bądź funkcją. an gu lar.isString,angular.isN um ber i angular.isD ate Metody pomocnicze do sprawdzania, czy zmienna jest łańcuchem, liczbą lub obiektem daty. Funkcji pomocniczych jest o wiele więcej. Wszystkie je można znaleźć w oficjalnej dokumentacji systemu AngularJS (https://docs.angularjs.org/api/ng/function).
Gdy uruchom isz opisywaną aplikację w przeglądarce, n a stronie ukaże się form ularz z dwoma polam i wejściowymi i inform acjam i o błędach dotyczącymi wymaganych pól. Podczas wpisywa nia tekstu zauważysz, że w iadom ość najpierw zm ieni się na inną, a potem form ularz zostanie uznany za wypełniony prawidłowo. Jak napisaliśm y w cześniej, fu nkcji compile używa się niezwykle rzadko, gdy trzeba wykonywać poważne operacje na strukturze D O M podczas działania aplikacji. W większości przypadków p o dobny efekt m ożna osiągnąć dzięki zastosowaniu transkluzji albo funkcji lin k . M im o to funkcja compile zapewnia dodatkowe m ożliwości na wypadek, gdyby były potrzebne.
Kompilacja
| 237
Łączenie początkowe i końcowe Funkcja link, w formie, w jakiej najczęściej ją piszemy (i zwracamy z funkcji compile), to tzw. funkcja łączenia końcowego. Podczas jej wykonywania wszystkie dyrektywy potomne są już skompilowane i odpowiednio połączone. Przekształcenia struktury DOM (nie dodawanie dy rektyw AngularJS, ale np. tworzenie wykresów) są w tym momencie bezpieczne. Ale gdybyśmy potrzebowali punktu zaczepienia, aby wykonać coś przed połączeniem potomków, możemy użyć tzw. funkcji łączenia początkowego. W momencie jej wykonania dyrektywy po tomne nie są jeszcze połączone, a przekształcenia struktury DOM nie są bezpieczne i mogą mieć dziwne skutki. Funkcje łączenia początkowego i końcowego można zdefiniować przez użycie obiektu zamiast funkcji link. Innymi słowy, zamiast pisać: j lin k :
f u n c t io n ( $ s c o p e , $elem ent, $ a t t r s )
(}
}
należy napisać: j lin k : j pre : f u n c t io n ( $ s c o p e , $elem ent, $ a t t r s ) p o s t: f u n c t io n ( $ s c o p e , $elem ent, $ a t t r s )
(}, (}
} }
To samo dotyczy zwrotu wartości z funkcji kompilującej, tzn. zamiast zwracać funkcję o nazwie p o st-lin k , można zwrócić obiekt z kluczami pre i post.
Priorytet i terminal Pozostały ju ż tylko dwie opcje tworzenia dyrektyw — p r i o r i t y i t e r m i n a l . O pcja p r i o r i t y służy do określania kolejności ewaluacji dyrektyw, jeśli jest ich kilka w jednym elemencie. Na przykład jeżeli w elemencie zadeklarujemy dyrektywy n g M o d e l , n g P a t t e r n i n g M i n l e n g t h , to m usim y za dbać, aby jako pierwsza została wykonana dyrektywa n g M o d e l . A zatem możemy nadać dyrektywie n g P a tte rn
niższy priorytet niż dyrektywie n g M o d e l .
Dom yślnie wszystkie tworzone przez nas dyrektywy m ają priorytet 0 . Im wyższa ta liczba, tym wyższy priorytet, a dyrektywy o wyższym priorytecie są kompilowane i łączone przed tymi o niż szym priorytecie. W definicji dyrektywy m ożna też używać słowa kluczowego t e r m i n a l , które służy do określania ostatniego priorytetu dyrektyw do wykonania. Ponadto ustawienie tej opcji na t r u e powoduje, że dyrektywy potom ne nie zostaną tknięte ani skompilowane. Dom yślnie opcja ta jest ustawiona na fa lse
. W szystkie dyrektywy w elemencie m ające taki sam priorytet zostaną wykonane, ponieważ
kolejność wykonywania dyrektyw o tym samym priorytecie jest niezdefiniowana.
2B8
I
Rozdział 1B. Zaawansowane opcje definicji dyrektyw
Integracja zewnętrzna O pisaliśm y ju ż wszystkie ważne składniki obiektu definicji dyrektywy oraz zaprezentow aliśm y kilka przykładów budowy skomplikowanych dyrektyw. W tym podrozdziale przedstawiamy ty pową procedurę integracji z aplikacji zewnętrznych dyrektyw wizualnych, np. wykresów i innych składników nastaw ionych nie na pobieranie danych, tylko na ich prezentowanie. Jeśli chodzi o proste wyświetlanie danych w formacie H TM L (jak w naszej dyrektywie s t o c k W id g e t ), to wystarczy użyć dyrektyw do wiązania danych systemu AngularJS. Ale w przypadku zewnętrznych kom ponentów wiązaniem danych należy zająć się samodzielnie. Jako twórcy dyrektyw wizualnych m am y do wykonania kilka czynności: 1. Przed uruchom ieniem dyrektywy m usim y poczekać na załadowanie odpowiedniej biblioteki. 2. M usim y pobrać dane z kontrolera i przekształcić je na form at odpowiedni dla naszego ze wnętrznego kom ponentu. 3. W yświetlamy dane za pom ocą zewnętrznego kom ponentu. 4. Gdy w AngularJS zm ienią się dane, aktualizujem y zewnętrzny kom ponent. 5. M usim y nasłuchiwać zdarzeń z zewnętrznego kom ponentu i przekazywać je do odpowied niego kontrolera przez wiązania funkcji. Teraz w ram ach przykładu zintegrujem y z własną aplikacją kom ponent Google Charts, który asynchronicznie ładuje swój interfejs API, i utworzymy dyrektywę p i e C h a r t , aby m óc wygodnie korzystać z tego kom ponentu. W iększość dyrektyw jest m niej skomplikowana, ale poniższy przy kład m a służyć jako szkielet do integracji z aplikacją każdego rodzaju dyrektywy wizualnej, jakiego możesz potrzebować. Jak zawsze zaczniemy od pliku in d ex .h tm l, aby zobaczyć, w jaki sposób chcemy używać planowanej dyrektywy: < h tm l> < t i t l e > A p l i k a c j a z w ykresam i G o o g le < / ti t le > < d iv n g - c o n t r o ll e r = " M a in C t r l as m a in C t r l" > < d iv > < b u tto n n g - c l i c k = "m a in C t r l. c h a n g e D a t a ( ) "> Zmień dane w ykresu < / b u tto n > < /d i v> < d iv p ie - c h a r t c h a rt - d a t a = "m a in C t r l .p ie C h a r tD a t a " c h a r t - c o n f ig = "m a in C t r l . p ie C h a r t C o n f ig "> < /d i v> < / d iv >
Integracja zewnętrzna
| 239
< s c r i p t s r c = " h t t p : / / w w w .g o o g le .c o m / jsa p i"> < / sc r i pt> < s c r i p t s r c = " h t t p s :/ / a j a x . g o o g le a p is . c o m / a ja x / li b s / a n g u la r j s / 1 . 3 . 1 1 / a n g u l a r . j s " > < / s c r i p t > < s c r i p t s r c = " a p p . j s " > < / s c r i pt> < s c r i p t s r c = " g o o g le C h a r t L o a d e r . j s " > < / s c r ip t > < s c r ip t s r c = "p i e C h a r t.js "> < / s c rip t>
Kod H TM L, w którym zastosowano dyrektywę pieC hart, jest bardzo prosty. Dyrektywy tej uży wamy w form ie atrybutu oraz przekazujemy do niej inform acje konfiguracyjne i potrzebne do wyświetlenia wykresu za pom ocą argumentów. Dzięki tem u dyrektywa nie jest związana na stałe z jed ną usługą lub jednym obiektem konfiguracji i m ożna używać je j wielokrotnie. Utworzyliśmy też przycisk służący do zm ieniania danych na wykresie. Jeśli chodzi o zależności skryptowe, to najpierw ładujem y asynchroniczny m echanizm wczytywa nia interfejsów A PI Google (Google Loader) (h ttp ://w w w .g oog le.com /jsapi) u łatwiający ładowanie interfejsów A PI, w tym przypadku Google Charts. Pokażm y również, ja k wykorzystać obietnice AngularJS do poczekania na załadowanie tego A PI przed rozpoczęciem rysowania wykresów. Teraz przyjrzymy się głównemu kontrolerow i aplikacji, który zawiera definicje danych i konfigu rację wykresu: // Plik: r13/directive-google-chart/app.js angul a r . m o d u le ( 'g o o g l e C h a rtA p p ', []) . c o n t r o l l e r ( 'M a i n C t r l ', [ f u n c t io n ( )
{
v a r s e lf = t h is ; s e lf . p ie C h a r t D a t a = [ { la b e l: { la b e l: { la b e l:
'P i e r w s z y ', v a lu e : 2 5 }, 'D r u g i ', v a lu e : 5 4 }, 'T r z e c i ', v a lu e : 75}
]; s e lf . p ie C h a r t C o n f ig = { t i t l e : 'W ykre s r a z , dwa, t r z y ', firstC o lu m n H e a d e r: 'L i c z n i k ', secondColum nHeader: 'R z e c z y w is t a w a r to ś ć ' }; s e lf . c h a n g e D a t a = f u n c t io n ( )
{
s e l f . p ie C h a r t D a t a [ 1 ] . v a l u e = 25; }; }]);
W pliku app.js zadeklarowaliśmy zm ienną pieChartData. Jest to tablica kluczy i wartości we wła snym form acie. W tym przykładzie treść ta je st wpisana na sztywno, ale m ogłaby być pobierana z serwera za pom ocą usługi $http. Ponadto w pliku tym zdefiniow aliśm y kilka ustawień k o n fi guracyjnych, np. nazwę wykresu i jego kolum n. Na końcu znajduje się prosta funkcja o nazwie changeD ata(), zm ieniająca wartość jednego elementu danych, aby m ożna było zobaczyć, czy m o dyfikacja ta spowoduje autom atyczną zmianę wykresu. Następna część aplikacji to asynchroniczny mechanizm wczytywania, który zadba o to, by dyrekty wa pieChart nie rozpoczęła rysowania wykresu, zanim nie zostanie wczytany interfejs API:
240
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
/ / Plik: r13/directive-google-chart/googleChartLoader.js a n g u la r . m o d u le ( 'g o o g le C h a r t A p p ') . f a c t o r y ( 'g o o g le C h a r t L o a d e r P r o m is e ', [ '$ q ', '$ r o o t S c o p e ', '$ w in d o w ', f u n c t io n ( $ q , $ ro o tSc o p e , $window)
{
/ / utworzenie odroczonego obiektu v a r d e fe rre d = $ q . d e f e r ( ) ;
// asynchroniczne załadow anie API Google Charts $ w in d o w . g o o g le . lo a d ( 'v i s u a l i z a t i o n ',
'1 ',
{ p a ck age s:
[ 'c o r e c h a r t '] ,
c a llb a c k : f u n c t io n ( )
{
// Po załadow aniu API wywołujemy fu n kcję resolve wewnątrz fu n kcji $apply, / / poniew aż zdarzenie m a miejsce poza cyklem życia AngularJS. $ r o o t S c o p e . $ a p p ly ( f u n c t io n ( ) d e fe rre d .re so lv e ();
{
}); } });
/ / zwrot obiektu obietnicy do dołączenia do łańcucha przez dyrektywę r e t u r n d e fe rre d .p ro m is e ; }]);
Fabryka g o o g l e C h a r t L o a d e r P r o m i s e ładuje bibliotekę wizualizacyjną jeden raz i zwraca obietnicę, którą można dołączyć do łańcucha, aby dowiedzieć się, kiedy zakończy się ładowanie tej biblioteki. W tym celu użyto usługi AngularJS $ q (zobacz punkt „Usługa $q” w rozdziale 6., jeśli potrzebujesz odświeżenia w iadom ości), którą wykorzystaliśm y też w rozdziale 10. do obsługi fu nkcji r e s o l v e w trasie. Usługa $q umożliwia nie tylko odrzucenie obietnicy za pom ocą funkcji $ q . r e j e c t ( d a t a ) , lecz również tworzenie własnych obietnic, z której to m ożliwości korzystamy teraz. Tworzymy obiekt d e f e r r e d , reprezentujący asynchroniczne zadanie, które zostanie wykonane w przy szłości. Do jego utworzenia użyliśmy funkcji $ q . d e f e r ( ) . Później zwracamy obiekt d e f e r r e d . p r o m is e , do którego użytkownicy interfejsu A PI m ogą dodać wywołanie . t h e n ( ) , aby otrzymać powiado m ienie o wykonaniu (lub odrzuceniu) tego asynchronicznego zadania. Potem wywołujemy API Google z argum entam i odpowiednimi do załadowania biblioteki wizualizacyjnej i przekazujemy mu funkcję zwrotną, która m a zostać powiadom iona o zakończeniu zadania. W funkcji zwrotnej rozwiązujemy ( r e s o l v e ) utworzony przez nas odroczony obiekt, co powoduje wykonanie wszystkich bloków . t h e n . Ale ponieważ ta funkcja zwrotna jest wywoływana poza cy klem życia AngularJS, m usim y opakować ją w funkcję $ r o o t S c o p e . $ a p p l y , by system AngularJS wiedział, że m a ponow nie narysow ać interfejs użytkownika i w razie potrzeby przeprowadzić kom pletny cykl obliczeniowy. Na koniec m ożem y przyjrzeć się dyrektywie p i e C h a r t , która integruje się z usługą g o o g l e C h a r t s ^ L o a d e r P r o m is e
i Google Charts:
/ / Plik: r13/directive-google-chart/pieChart.js a n g u la r . m o d u le ( 'g o o g le C h a r t A p p ') . d i r e c t i v e ( 'p i e C h a r t ', [ 'g o o g le C h a r t L o a d e r P r o m is e ', f u n c t i o n (g o o g le C h a rtL o a d e rP ro m ise ) { v a r co n v e rtT o P ie C h a rtD a ta T a b le F o rm a t = fu n c tio n (firs tC o lu m n N a m e , secondColumnName, data)
{
Integracja zewnętrzna
| 241
v a r p ie C h a r t A r ra y = [[firstC o lu m n N a m e , secondColum nNam e]]; f o r (v a r i = 0; i < d a t a .le n g t h ; i+ + ) { p ie C h a r t A r ra y .p u s h ( [ d a t a [ i] . l a b e l , d a t a [ i] .va l u e ] ); } r e t u r n g o o g l e . v i s u a l i z a t io n . a r ra y T o D a t a T a b le ( pi e C h a r t A r r a y ) ; }; retu rn { r e s t r i c t : 'A ', scope: { c h a rtD a ta : ' = ' , c h a r t C o n fig : '= ' }, lin k :
f u n c t io n ( $ s c o p e , $elem ent)
{
g o o g le C h a r tL o a d e rP ro m is e . t h e n (fu n c ti o n () { v a r c h a rt = new g o o g l e . v i s u a l i z a t io n . P i e C h a r t ( $ e le m e n t [0 ]); $ s c o p e . $ w a t c h ( 'c h a r t D a t a ', fu n c t io n (n e w V a l, o ld V a l) v a r c o n f ig = $ s c o p e . c h a r t C o n fig ; i f (newVal) { c h a rt.d ra w ( co n v e rtT o P i e C h artD ataT a b le F o rm at( c o n f ig .fir s t C o lu m n H e a d e r , co n fig .se co n d C o lu m n H e a d e r, new Val), { t i t l e : $ s c o p e . c h a r t C o n f ig . t i t l e } ) ;
{
} },
tru e );
}); } }; }]);
Jeśli pom inąć wywołania A PI Google Charts, to dyrektywa p i e C h a r t na poziom ie koncepcyjnym nie jest wcale taka skomplikowana. O to szczegółowy opis je j budowy: 1. Dyrektywa p i e C h a r t zależy od wcześniej zdefiniowanej przez nas usługi, więc wstrzykujemy je j tę usługę. 2. Z definiow aliśm y fu n k cję o nazwie c o n v e r t T o P i e C h a r t D a t a T a b l e F o r m a t , p o b ierającą dane z naszego kontrolera i konw ertującą je na form at, w którym m ogą zostać przesłane do A PI Google Charts. 3. D efiniujem y całkiem standardową dyrektywę z izolowanym zakresem definiującym atrybuty, które m ają zostać do niej przekazane. 4. W fu n k cji l i n k w ykorzystujem y ob ietn icę zw róconą z usługi i w ykonujem y sw oją pracę w procedurze obsługi pow odzenia w bloku t h e n tej obietnicy. D zięki tem u m am y pewność, że nie wywołamy A PI Google Charts, zanim nie zostanie ono w pełni załadowane. 5. W procedurze obsługi powodzenia obietnicy tworzymy egzemplarz wykresu kołowego w ele mencie, w którym aktualnie się znajdujemy. Dzięki temu nie musimy szukać jakiegoś losowego elem entu ani wykorzystywać selektorów identyfikatorow ych, przez które nasza dyrektywa byłaby trudniejsza w użyciu.
242
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
6. Następnie dodajem y czujkę na polu c h a r t D a t a w zakresie oraz przekazujemy je j funkcję do wywołania jako drugi argum ent i wartość logiczną t r u e jako trzeci argument. W ten sposób stosujem y do $ s c o p e . c h a r t D a t a technikę zwaną w AngularJS głęboką obserwacją (ang. d eep w atch ). Jeśli wartość obserwowanego pola (albo jakiegokolw iek elementu w n im ) się zmieni, nastąpi wywołanie funkcji. 7. Funkcja c h a n g e jest wywoływana zarówno ze starą, ja k i nową wartością. Gdy otrzym am y p o prawną nową wartość, rysujem y wykres po uprzednim przekonwertowaniu danych z form atu przekazanego dyrektywie na form at rozpoznawany przez Google Charts. 8. Kiedy dane w AngularJS ulegną zmianie (z inicjatywy użytkownika lub serwera), funkcja ta zostanie wywołana automatycznie, więc nie m usim y nic więcej robić, aby zapewnić aktuali zowanie wykresu. Jeśli teraz otworzysz plik in d ex .h tm l w przeglądarce, zobaczysz wykres kołow y przedstaw iający początkow e dane. M ożesz kliknąć znajdujący się na stronie przycisk, żeby zaktualizow ać dane w kontrolerze. Inform acje te są przekazywane bezpośrednio przez referencję, więc dyrektywa je otrzyma i nastąpi wywołanie funkcji je obserwującej. W efekcie nastąpi automatyczna aktualiza cja wykresu najnowszym i wartościam i i interfejsu użytkownika. Gdybyśmy tylko chcieli dodać element danych albo restrykcyjnie kontrolować sposób i czas aktuali zacji wykresu, moglibyśmy dodać odpowiednią logikę do czujki. Poniżej przedstawiamy zestawienie czynności, które należy wykonać w prawie każdej dyrektywie nastaw ionej na prezentację danych: • odczekanie na załadowanie interfejsu API; • przekazanie danych do dyrektywy; • przekonwertowanie danych na odpowiedni form at i ich początkowa prezentacja; • obserwowanie danych i aktualizowanie interfejsu użytkownika w razie potrzeby.
Najlepsze praktyki W iesz już, ja k tw orzyć praktycznie wszystkie rodzaje dyrektyw o dowolnym stopniu złożoności, więc teraz chcielibyśmy, abyś poznał kilka zasad, których warto przestrzegać, by tw orzone przez Ciebie dyrektywy działały zgodnie z założeniam i i możliwie szybko w każdych warunkach.
Zakresy Jeśli w swojej dyrektywie dodajesz zmienne i funkcje do zakresu w elemencie l i n k lub c o n t r o l l e r , to powinieneś ustawić klucz s c o p e w obiekcie definicji dyrektywy na t r u e albo utworzyć izolowany zakres dla tej dyrektywy. Na przykład powiedzmy, że nasz kontroler zawiera zm ienną logiczną o nazwie s e l e c t e d , okre ślającą, czy pewne pole wyboru je st zaznaczone, czy nie. Gdyby nasze dyrektywy t a b s i t a b nie tw orzyły now ego zakresu bądź nie używały zakresu izolow anego, to przesłoniłyby zm ienną se l e cte d
z kontrolera, co powodowałoby najróżniejsze niepożądane skutki.
Najlepsze praktyki
| 243
Jeżeli dyrektywa wymaga dostępu do fu nkcji i zm iennych z zakresu nadrzędnego, to m am y do wyboru dwie m ożliw ości: • Utworzyć zakres potom ny i dodać do niego dowolne zmienne i funkcje. W tym celu w obiekcie definicji dyrektywy m ożna ustawić opcję s c o p e na t r u e . Ale jeśli zakres nadrzędny zawiera jakiekolw iek funkcje lub zm ienne, to będą one dostępne w dyrektywie. • Utworzyć zakres izolowany oraz przekazywać wszelkie zm ienne i funkcje przy użyciu wiąza nia danych i fu nkcji. Jest to idealne rozw iązanie, ponieważ wyklucza ryzyko, że dyrektywa będzie potrzebowała do działania określonej zm iennej albo funkcji z zakresu nadrzędnego. Dyrektywy z zakresem izolowanym najlepiej nadają się do wielokrotnego wykorzystania.
Sprzątaj i niszcz Gdy użytkownik korzysta z wiązań i innych dyrektyw, system AngularJS dodaje procedury nasłu chowe i czujki um ożliw iające m u aktualizow anie interfejsu użytkownika. A by żadna z n ich nie wyciekła ani nie pozostała w tyle, AngularJS usuwa je w m om encie niszczenia ich zakresów lub elementów. Kiedy program ista tworzy dyrektywę AngularJS z własnym zakresem (potom nym bądź izolowa nym ), to wszelkie czujki dodane na tym zakresie i procedury nasłuchowe dodane na elemencie przekazanym do tej dyrektywy są autom atycznie kasowane w chw ili zniszczenia tej dyrektywy w interfejsie użytkownika. System AngularJS nie może jednak skasować procedur nasłuchu zdarzeń dodanych przez nas do elem entów znajdujących się poza zakresem ani kodu H TM L dyrektywy. Gdy dodamy do aplikacji te proced u ry albo czu jki, posprzątanie ich po zniszczeniu dyrektywy je s t naszym zadaniem . N asłuch zdarzeń zniszczenia dyrektywy m ożna prow adzić na dwa sposoby: N asłuchiw anie zd arzenia $destroy n a zakresie Jak pokazaliśmy wcześniej, procedury nasłuchowe m ożna dodawać dla samego zakresu. Każdy zakres rozgłasza zdarzenie o nazwie $ d e s t r o y , które stanowi sygnał, że za chwilę zostanie znisz czony i skasowany. Zdarzenia tego może nasłuchiwać każdy kontroler i każda dyrektywa, aby wykonać dodatkowe czynności, gdy ono nastąpi. W procedurze obsługi zdarzenia $ d e s t r o y muszą zostać skasowane wszelkie procedury nasłuchowe, które dodamy ręcznie, lub interwały i lim ity czasu, które aktualnie trwają. Poniżej znajduje się prosta procedura nasłuchowa: $ s c o p e . $ o n ( '$ d e s t r o y ',
f u n c t io n ( )
{
/ / miejsce na kod porządkow y });
N asłuchiw anie zd arzenia $destroy n a elem encie Jeśli zakres je st odziedziczony (nie je st now y ani izolow any), a m im o to m usim y wykonać czynności porządkowe w m om encie niszczenia dyrektywy, rozwiązaniem jest nasłuchiwanie zdarzenia $ d e s t r o y na elemencie. Jest to zdarzenie jQ u ery wyzwalane przez AngularJS przed usunięciem elementu ze struktury DOM . Przykładowa procedura obsługi tego zdarzenia może wyglądać tak: $ e le m e n t . $ o n ( '$ d e s t r o y ',
f u n c t io n ( )
{
/ / miejsce na kod porządkow y });
244
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
Czujki W systemie AngularJS istnieje m ożliwość dodania własnych procedur nasłuchu zdarzeń (zwa nych czujkam i — ang. w atch er — w term inologii AngularJS) do zm iennych i funkcji w zakresie. Są one urucham iane przez A ngularJS w m o m en cie zm odyfikow ania obserw ow anej zm ien nej i program ista w takim przypadku m a dostęp zarówno do starej, ja k i now ej wartości. Istnieje kilka rodzajów czujek i warto wiedzieć, jakie cechy m a każdy z nich: $watch Standardowa czujka, która pobiera: • łańcuch będący nazwą zm iennej z zakresu; • funkcję, której wartość zwrotna jest obliczana. W obu przypadkach, jeśli wartość ulegnie zmianie (wykonywany jest prosty płytki test), nastę puje wywołanie przekazanej do czujki jako drugi argum ent funkcji ze starą i nową wartością. G łęb oka w ersja $watch Taka sama czujka jak standardowa, ale przyjmująca jako trzeci argument logiczną wartość true. Zmusza ona system AngularJS do rekurencyjnego sprawdzenia wszystkich obiektów i kluczy w obiekcie lub zm iennej i użycia funkcji an g u lar.eq u als do porównywania obiektów. Oczy wiście czujka ta wykryje wszystkie zm iany, ale zużyje też więcej cyklów procesora. Dlatego nie należy je j nadużywać w swoich aplikacjach. Lepszym rozwiązaniem może być utworzenie zm iennej logicznej sygnalizującej wewnętrzne zmiany i obserwowanie tej zm iennej. $watchCollect1on Nieco zoptymalizowana wersja czujki przeznaczona dla tablic. Pobiera podobne argumenty ja k $watch, ale przekazywana wartość powinna być łańcuchem . Funkcja jest wywoływana za każdym razem, gdy w tablicy zostaje dodany, usunięty lub przesunięty element. Czujka ta nie obserwuje zm ian indywidualnych własności elem entów w tablicy.
Funkcje $apply i $digest N ajczęściej występującym błędem przy integracji program ów z zewnętrznymi kom ponentam i jest brak aktualizacji interfejsu użytkownika, m im o że wszystko zostało popraw nie skonfigurow ane i połączone. Bardzo często przyczyną tych problem ów jest brak wywołania funkcji $apply() albo ręcznego uruchom ienia cyklu obliczeniowego za pom ocą wywołania funkcji $ d ig e s t ( ) . Jeśli pracujesz z zewnętrznymi kom ponentam i, zawsze pam iętaj, że w grze są dwa osobne cykle życia. Pierwszy to cykl życia AngularJS, który odpowiada za aktualizowanie interfejsu użytkowni ka, a drugi to cykl życia kom ponentu zew nętrznego. N a styku tych dwóch cyklów program ista musi powiadomić system AngularJS, że zmieniło się coś poza jego cyklem życia i że należy dokonać aktualizacji interfejsu użytkownika. I do tego służy funkcja $sco p e.$ a p p ly (), która rozpoczyna cykl obliczeniowy na $rootScope. Czasami wszystkim automatycznie zajm uje się inne zdarzenie w AngularJS, ale jeśli aktualizujesz zmienne zakresowe w odpowiedzi na zewnętrzne zdarzenia, pamiętaj, aby ręcznie wywołać funkcję $scop e.$ap p ly() lub $ s c o p e .$ d ig e s t().
Najlepsze praktyki
| 245
Podsumowanie W rozdziale tym zgłębiliśmy tajn ik i najbardziej skomplikowanych elem entów systemu AngularJS i opisaliśmy niektóre rzadziej używane, choć bardzo przydatne składniki i dyrektywy. Utworzyli śmy widżet giełdowy pobierający niestandardowe szablony, który dzięki tem u m ożna dostosować do indyw idualnych potrzeb za pom ocą prostej tran skluzji szablonów. Utw orzyliśm y też bardzo uproszczoną wersję jednopow tórzeniow ej pętli, wykorzystując zaawansowaną transkluzję i funk cje transkluzji. Ponadto pokazaliśmy, ja k nawiązać kom unikację między dyrektywami t a b s i t a b za pomocą kontrolerów dyrektyw oraz jak zastosować istniejące kontrolery i dyrektywy, np. ngM od e l , do tworzenia dyrektyw wejściowych typu suwaki i walidatory. W dalszej części rozdziału opisaliśmy konfigurację obiektu definicji dyrektywy reprezentującego deklaratywną dyrektywę f o r m - e le m e n t , generującą dynamiczne szablony przy użyciu etapu kom pilacji dyrektyw. Później udowodniliśmy, że utworzenie dyrektywy wyświetlającej wykres kołowy za pom ocą API Google Charts nie jest trudne i opisaliśmy podstawowe kwestie dotyczące budowy kom ponentów wizualnych oraz integrowania z aplikacją zewnętrznych widżetów interfejsu użytkownika. Na za kończenie przedstawiliśmy kilka porad na tem at tworzenia solidnych dyrektyw — przestrzeganie tych porad pozwala uniknąć kłopotów z wydajnością i dziwnych błędów. Znasz ju ż wszystkie najważniejsze rdzenne składniki systemu AngularJS. W następnym rozdziale przedstawiamy m etody pisania kom pletnych testów dla AngularJS przy użyciu narzędzia do wy konywania testów Protractor.
246
|
Rozdział 13. Zaawansowane opcje definicji dyrektyw
____________________ ROZDZIAŁ 14.
Testowanie kompleksowe
P o z n a ł e ś j u ż w s z y s t k i e t r y b y w c h o d z ą c e w s k ł a d m a s z y n y b ę d ą c e j a p lik a c j ą A n g u l a r J S , c z y l i k o n t ro le r y , w id o k i , u s łu g i, f ilt r y i d y r e k t y w y . W i e s z też, j a k w a ż n e je st t e s t o w a n ie j e d n o s t k o w e , i u m ie s z p rz e te sto w a ć in d y w id u a ln ie k a ż d ą czę ść s y s t e m u A n g u la r J S . D o b r y z e sta w te stó w j e d n o s t k o w y c h p o z w a l a z a o s z c z ę d z i ć o g r o m n ą i l o ś ć c z a s u , k t ó r y in a c z e j t r z e b a b y b y ł o p r z e z n a c z y ć n a d e b u g o w a n ie , z a p o b ie g a n i e p o w s t a w a n i u b ł ę d ó w i w y s z u k i w a n i e r e g re s ji. A l e t e s t y j e d n o s t k o w e s p r a w d z a j ą s ię t y lk o d o p e w n e g o s t o p n ia . Z a i c h p o m o c ą m o ż n a s ię u p e w n ić , ż e a p lik a c j a d z ia ł a p o p r a w n i e , le c z
p rz y z a ło ż en iu , ż e s e r w e r d z ia ł a w o k r e ś l o n y s p o s ó b . P r z e
k o n a l i ś m y s ię o t y m p o d c z a s t e s t o w a n i a u s ł u g i a s y n c h r o n i c z n y c h w y w o ł a ń s e r w e r o w y c h w r o z d z ia l e 7., w k t ó r y m i m i t o w a l i ś m y s e r w e r z a p o m o c ą u s ł u g i $ h t t p B a c k e n d . U m o ż l i w i ł o n a m to s z y b k i e w y k o n y w a n i e t e s t ó w j e d n o s t k o w y c h , k t ó r e b y ł y n i e z a w o d n e , s t a b il n e i b a r d z o s z y b k i e . T e s t y te p o z w a l a j ą p r z e c h w y c i ć l o g i k ę k o n t r o l e r ó w i u s ł u g , a le c o z r o b ić , j e ś l i s e r w e r z m i e n i a w a r t o ś ć z w r o t n ą ? A l b o c o s ię s t a n ie , j e ż e li z m i e n i ą s ię a d r e s y U R L n a s e r w e r z e ? C o z f o r m a t o w a n i e m i w y ś w i e t l a n i e m t r e ś c i H T M L , z w ł a s z c z a g d y z r o b i m y lit e r ó w k ę w w y r a ż e n i u n g - b i n d ? A b y w y k r y ć t a k ie b ł ę d y , m u s i m y n a p i s a ć t e s t y k o m p l e k s o w e , k t ó r e u r u c h a m i a j ą p r z e g l ą d a r k ę , u r u c h a m i a j ą n o r m a l n ą w e r s ję a p lik a c j i i k lik a j ą w n ie j ta k , j a k b y t o r o b i ł n o r m a l n y u ż y t k o w n i k . D o t e g o p o t r z e b n e je st n a r z ę d z i e o n a z w ie P r o t r a c t o r ( h ttp s://g ith u b .co m /a n g u la r/p ro tra ctor ). W
t y m r o z d z ia l e p r z e d s t a w i a m y b u d o w ę b a r d z o p r o s t e g o t e s t u k o m p l e k s o w e g o d la d e m o n s t r a
c y jn e j a p lik a c ji. D e f i n i u j e m y k o n f i g u r a c j ę n a r z ę d z i a P r o t r a c t o r , p i s z e m y k o m p l e k s o w y test i s p r a w d z a m y , j a k d z ia ła . P o n a d t o o p i s u j e m y w s t ę p n ą k o n f i g u r a c j ę i w y m a g a n i a , j a k ie n a l e ż y s p e ł n ić , b y w y k o n a ć te testy, j a k r ó w n ie ż p r e z e n t u j e m y n a jle p sz e p r a k t y k i p r a c y z n im i. P o p r z e s t u d io w a n iu te g o r o z d z i a ł u b ę d z i e s z z n a ł n a r z ę d z ie P r o t r a c t o r i b ę d z i e s z w ie d z ia ł, j a k p i s a ć t e s t y p r z y j e g o u ż y c iu .
Do czego służy Protractor D l a c z e g o w y b r a l i ś m y n a r z ę d z i e P r o t r a c t o r , a n i e j a k ie ś i n n e ? P i e r w s z y m n a r z ę d z i e m m a j ą c y m u ł a t w i ć p i s a n i e k o m p l e k s o w y c h t e s t ó w d la a p lik a c j i A n g u l a r J S b y ł A n g u l a r J S S c e n a r i o R u n n e r
( h ttp ://co d e.an g u larjs.o rg /L 2 .1 6 /d o cs/g m d e/e2 e-testin g ) . B y ł t o k o m p l e t n y s y s t e m d o w y k o n y w a n ia k o m p le k s o w y c h te stó w r o z p o z n a w a n y p rz e z A n g u la r J S , d z ię k i c z e m u te sty w y k o n y w a n e p r z y j e g o u ż y c i u b y ł y s t a b iln e i o b lic z a ln e .
247
Zrozum ieliśm y, że symulowanie czynności użytkowników, takich ja k klikanie i wpisywanie po przez JavaScript, nie było idealnym rozw iązaniem i nie pozwalało w pełni odtworzyć norm alnej sesji użytkowej. Dlatego tw órcy narzędzia Protractor postanowili wykorzystać jako podstawę coś takiego ja k Selenium W ebD river (h ttp ://d ocs.selen iu m h q.org /p ro jects/w ebd riv er/), czyli rozwiąza nie działające na poziom ie systemu operacyjnego i pozwalające naśladować prawdziwe kliknięcia myszą oraz naciśnięcia klawiszy. Ale jednocześnie nadal chcem y unikać jednego z największych problem ów dotyczących kom plek sow ych testów aplikacji A JA X , czyli oczekiw ania n a załadowanie strony. Podczas przeglądania norm alnej strony przez użytkownika bez problem u m ożem y sprawdzić, kiedy zakończyło się ła dow anie strony, i w odpow iedzi na kliknięcie od n ośnika m ożem y d okon ać je j przeładow ania w całości. Dzięki temu dokładnie wiemy, kiedy strona zostaje załadowana, i m ożem y kontynu ować testowanie. Z aplikacjami jednostronicowym i jest inaczej, ponieważ ładowana jest tylko jedna strona. Wszystkie dane mogą być (i z reguły są) pobierane asynchronicznie nawet po zakończeniu ładowania strony. Skąd w takim razie wiadomo w teście, kiedy sprawdzić, czy określony elem ent danych został po kazany? M am y dwie możliwości: • Czekamy przez pewien arbitralnie określony czas po załadowaniu strony lub kliknięciu od nośnika — około pięciu sekund. • Przed wykonaniem testów czekamy na pojawienie się na stronie określonego elementu. Obie te metody są bardzo zawodne. Wystarczy, że jakieś wywołanie serwerowe potrwa 5,1 sekundy, i test zakończy się niepowodzeniem . Potem rozpoczynam y oczekiwanie na następne wykonanie testu, nawet jeśli w teście nie uda się wyłapać tych niedeterministycznych niepowodzeń. I w końcu przestajem y ufać testom kompleksowym. P ro tracto r spraw ia, że problem oczekiw ania przez arb itraln ą ilość czasu na zdarzenia znika. Narzędzie to jest zbudowane na bazie W ebD rivera, ale rozpoznaje AngularJS. Zatem jeśli zostanie kliknięty przycisk i wysłane zostanie wywołanie serwerowe, Protractor wie, że musi poczekać na zwrot z tego wywołania, zanim wykona dalszą część testu. Dzięki temu program ista może skupić się na pisaniu testu, który zostanie wykonany podobnie ja k w przypadku działania użytkownika, bez potrzeby stosowania warunków i lim itów czasu dotyczących pojawiania się lub znikania okre ślonych elementów.
Konfiguracja wstępna Protractor to pakiet NodeJS, więc m ożna go zainstalować za pom ocą polecenia npm (oczywiście do tego konieczna jest instalacja N odeJS): sudo npm i n s t a l l
-g p ro tra c to r
Polecenie to spowoduje zainstalowanie narzędzia Protractor wraz ze wszystkimi jego zależnościami jako globalnego pakietu do użytku w różnych projektach.
248
|
Rozdział 14. Testowanie kompleksowe
Dodatkowo potrzebujem y narzędzia W ebD river do urucham iania i kontrolow ania przeglądarek, w których w ykonujem y testy jednostkowe. Po zainstalowaniu Protractora otrzym ujem y skrypty potrzebne do pobrania i zainstalowania także W ebD rivera. W ykonaj tylko następujące polecenie: sudo w e bd rive r-m an ag e r update
W tym m o m en cie m am y wszystkie narzędzia potrzebne do w ykonyw ania testów P rotractor. A wykonywanie tych testów sprow adza się do w ykonania poniższego polecenia: p ro tra c to r p a th / to / p ro tra c to r.c o n f.js
Jak wygląda zawartość pliku p rotractor.con f.js? Dowiesz się tego w następnym podrozdziale.
Konfiguracja narzędzia Protractor K onfiguracja narzędzia Protractor znajduje się w pliku JavaScript zawierającym wszystkie opcje potrzebne do wykonywania testów kom pleksowych za pom ocą tego programu. Zaliczają się do nich następujące ustawienia: • adres serwera; • adres Selenium W ebD river, na którym m ają być wykonywane testy; • testy do wykonania; • lista przeglądarek do użycia w testach. To oczywiście nie wszystkie ustawienia. Poniżej przedstawiamy przykładową konfigurację zawiera jącą większość powszechnie używanych opcji, którą wykorzystamy w przykładach w tym rozdziale: / / Plik: r14/protractor.conf.js e x p o r t s . c o n f ig = {
/ / adres serwera Selenium s e le n i um Address:
' h t tp :/ / lo c a lh o s t :4 4 4 4 / w d / h u b ',
/ / adres testowanego serwera b a se U rl:
' h t t p : / / lo c a l h o s t : 8 0 0 0 / ',
/ / opcje do przekazan ia do WebDrivera c a p a b ilit ie s :
{
'b ro w se rN a m e ':
'ch ro m e '
},
/ / wzorce specyfikacji odnoszą się do lokalizacji pliku specyfikacji i mogą zawierać wzorce globalne sp e c s:
[ ' * S p e c * . j s '] ,
/ / opcje do przekazania do węzła Jasm ine jasm in e N od e O p ts:
{
sh o w C o lo rs: t ru e / / włącza kolory w raporcie w wierszu poleceń } };
Konfiguracja narzędzia Protractor
| 249
Je st t o n a j p r o s t s z y m o ż l i w y p l i k k o n f i g u r a c y j n y n a r z ę d z i a P r o t r a c t o r . Z a w i e r a o n n a s t ę p u j ą c e u s t a w ie n ia : • S e r w e r S e l e n i u m d z i a ł a l o k a l n i e n a p o r c ie 4 44 4. • S e r w e r d z ia ł a p o d a d r e s e m
h ttp ://lo calh o st:8 0 0 0 / .
• A u t o m a t y c z n ie m a b y ć u r u c h a m ia n a p r z e g lą d a r k a C h r o m e . • P lik
spec.js z a w i e r a k o d t e s t u k o m p l e k s o w e g o .
• O p c j a k o n f i g u r a c y j n a J a s m in e w łą c z a j ą c a k o l o r y w w i e r s z u p o le c e ń . S z c z e g ó ło w e in f o r m a c j e n a t e m a t o p c ji k o n f ig u r a c y j n y c h n a r z ę d z ia P r o t r a c t o r m o ż n a z n a le ź ć w p l i k u k o n f i g u r a c j i re fe r e n c y j n e j ( https://g ithu b.com /an g u lar/p rotractor/blob/m aster/d ocs/referen ceC on f.js ) w p o r t a lu G i t H u b . C o trz e b a te ra z z ro b ić , a b y w y k o n a ć te st? P r z e jd ź d o f o ld e r u
r14 z p l i k ó w p o b r a n y c h z s e r w e r a
F T P i w y k o n a j n a s tę p u ją c e c z y n n o ś c i: 1. U r u c h o m lo k a ln ie S e l e n i u m ( m o ż n a t o z r o b i ć z a p o m o c ą p o l e c e n ia w e b d r iv e r - m a n a g e r s t a r t ) . 2. U r u c h o m lo k a ln ie s e r w e r ( w t y m p r z y p a d k u n o d e s e r v e r . j s ) . 3. U r u c h o m n a r z ę d z ie P r o t r a c t o r ( p r o t r a c t o r t e s t / e 2 e / p r o t r a c t o r . c o n f . j s ) . A l e n a j p i e r w p r z y j r z y m y s ię n a s z e m u t e s t o w i.
Test kompleksowy T e s t y P r o t r a c t o r w y k o r z y s t u j ą tę s a m ą s k ł a d n i ę J a s m in e , k t ó r e j u ż y w a m y d o t e s t ó w j e d n o s t k o w y c h , w ię c m o ż e m y p o s ł u g i w a ć s ię b l o k a m i d e s c r i b e d la z b i o r ó w t e s t ó w i i n d y w i d u a l n y m i b l o k a m i i t d la p o j e d y n c z y c h testów . D o d a t k o w o P r o t r a c t o r u d o s t ę p n ia k ilk a z m ie n n y c h g lo b a ln y c h , k tó re są p o trz e b n e d o p is a n ia te stó w k o m p le k s o w y c h : bro w se r Je st t o o p a k o w a n i e d l a W e b D r i v e r a u m o ż l i w i a j ą c e b e z p o ś r e d n i ą in t e r a k c j ę z p r z e g lą d a r k ą . O b ie k t u te g o u ż y w a m y d o w c h o d z e n ia n a r ó ż n e s t r o n y i s p r a w d z a n ia in f o r m a c j i n a s tro n a c h . e le m e n t O b i e k t e le m e n t to f u n k c j a p o m o c n i c z a u m o ż liw ia j ą c a z n a j d o w a n ie e le m e n t ó w H T M L i in te ra k c ję z n i m i . Z n a j d u j e o n a e le m e n t y j a k o a r g u m e n t y , a n a s t ę p n ie z w r a c a e le m e n t , z k t ó r y m m o ż n a w c h o d z i ć w in t e r a k c j e , n p . k li k a j ą c g o i w y s y ła j ą c d o n i e g o z d a r z e n i a n a c i ś n i ę c i a k la w is z y . by Je st t o o b ie k t z a w ie r a j ą c y k o l e k c j ę s t r a t e g ii z n a j d o w a n i a e le m e n t ó w . E l e m e n t y m o ż n a w y s z u k iw a ć z a p o m o c ą s t a n d a r d o w y c h s t r a t e g ii W e b D r i v e r a , c z y li w e d ł u g i d e n t y f ik a t o r ó w i k la s C S S . P o n a d t o P r o t r a c t o r d o d a j e k i l k a w ł a s n y c h s tr a t e g ii p o z w a la j ą c y c h w y s z u k i w a ć e le m e n t y w e d ł u g m o d e l u (m o d e l), w i ą z a n ia ( b i n d i n g ) i p ę t li ( r e p e a t e r ) , k t ó r e s ą t y p o w e d la s y s t e m u A n g u la r J S . B e z z b ę d n e g o d a l s z e g o g a d a n i a s p ó j r z m y t e r a z n a t e s t d l a a p l ik a c j i t r a s u j ą c e j , k t ó r ą n a p i s a l i ś m y w r o z d z i a l e 10. (jej k o d z n a j d u j e s ię w f o ld e r z e
r1 4 ). A b y u r u c h o m i ć tę a p lik a c j ę , n a j p i e r w z a i n
s t a lu j p o t r z e b n e p a k i e t y z a p o m o c ą p o l e c e ń npm i n s t a l l i n o d e s e r v e r . j s :
250
|
Rozdział 14. Testowanie kompleksowe
/ / Plik: rM/simpleRoutingSpec.js d e s c r i b e ( 'R o u t i n g T e s t ',
f u n c t io n ( )
{
it ( 'P o w i n i e n w y ś w ie t lić d ru żyn y na p ie rw sz e j s t r o n i e . ',
f u n c t io n ( )
{
// otwiera stronę z listą drużyn b ro w s e r.g e t('/ ') ;
// sprawdzenie, czy w pętli jest pięć wierszy v a r rows = e le m e n t . a ll( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L i s t C t r l. t e a m s ') ) ; e x p e c t ( r o w s . c o u n t ( ) ) . t o E q u a l( 5 ) ;
// sprawdza szczegóły pierwszego wiersza v a r firstR o w R a n k = elem ent( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L is t C t r l. t e a m s ') . r o w (0 ). c o lu m n ( ' t e a m . r a n k ') ); v a r firstRow N am e = elem ent( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L is t C t r l. t e a m s ') . r o w (0 ). c o lu m n ( ' te a m .n a m e ')); e x p e c t ( f ir s t R o w R a n k . g e t T e x t ( ) ) . toEqual ( ' 1 ' ) ; e x p e c t ( fir s t R o w N a m e . g e t T e x t () ) . toEqual ( 'H i s z p a n i a ') ;
// sprawdzenie szczegółów ostatniego wiersza v a r lastRow R ank = elem ent( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L is t C t r l. t e a m s ') . r o w (4 ). c o lu m n ( ' t e a m . r a n k ') ); v a r lastRowName = elem ent( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L is t C t r l. t e a m s ') . r o w (4 ). c o lu m n ( ' te a m .n a m e ')); e x p e c t ( la s t R o w R a n k . g e t T e x t ( ) ) . t o E q u a l( '5 ') ; e x p e c t ( la s t R o w N a m e . g e t T e x t () ) .t o E q u a l('U r u g w a j ') ;
// sprawdzenie, czy odnośnik logowania jest wyświetlony, a odnośnik wylogowywania jest ukryty e x p e c t ( e l e m e n t ( b y . c s s ( '. l o g i n - l i n k ') ) . i s D i s p l a y e d ( ) ) .to B e (tru e ); e x p e c t ( e le m e n t ( b y . c s s ( '. l o g o u t - l i n k ' ) ) . i s D i s p la y e d ( ) ) . t o B e (fa ls e ) ; }); i t ( 'P o w in i e n u m o żliw ia ć lo g o w a n ie . ',
f u n c t io n ( )
{
// przejście na stronę logowania b r o w s e r . g e t ( '# / l o g i n ') ; v a r username = elem ent( b y .m o d e l( ' lo g i n C t r l. u s e r . u s e r n a m e ') ); v a r passw ord = elem ent( b y .m o d e l( ' lo g i n C t r l . u s e r . p a s s w o r d ') );
/ / wpisuje nazwę użytkownika i hasło u s e rn a m e . s e n d K e y s ( 'a d m in '); p a s s w o r d . s e n d K e y s ('a d m in ');
// klika przycisk logowania e le m e n t ( b y . c s s ( ' . b t n . b t n - s u c c e s s ') ) . c l i c k ( ) ;
Test kompleksowy
| 251
/ / sprawdza, czy użytkownik na pew no został przekierowany e x p e c t ( b r o w s e r .g e t C u r r e n t U r l()) . t o E q u a l ( 'h t t p : / / lo c a l h o s t : 8 0 0 0 / # / ');
/ / sprawdzenie, czy odnośnik logowania jest ukryty, a odnośnik wylogowywania jest wyświetlony e x p e c t ( e le m e n t ( b y . c s s ( ' . l o g i n - l i n k ') ) . i s D i s p l a y e d ( ) ) . t o B e ( fa ls e ) ; e x p e c t ( e le m e n t ( b y . c s s ( ' . l o g o u t - l i n k ') ) . i s D i s p l a y e d ( ) ) . t o B e ( t r u e ); }); });
W przykładzie tym znajdują się dwa testy. Pierwszy test: • O tw iera stronę główną aplikacji z drużynami piłkarskimi. • Pobiera wszystkie wiersze za pom ocą pętli i sprawdza, czy na stronie głównej jest pięć wierszy. • Pobiera nazwę i ranking z pierwszego wiersza i sprawdza, czy wszystko jest w porządku. • Pobiera nazwę i ranking z ostatniego wiersza i sprawdza, czy wszystko jest w porządku. • Sprawdza, czy odnośnik logowania jest widoczny, a odnośnik wylogowywania jest ukryty. Zatem pierwszy test dotyczy wyłączenie renderowania i logiki i pozwala sprawdzić, czy aplikacja poprawnie łączy się z serwerem oraz czy prawidłowo pobiera i wyświetla treść. Drugi test dotyczy interakcji program u z użytkownikiem i sprawdza: • otwieranie strony logowania; • czy nazwa użytkownika i hasło są wprowadzane do odpowiedniego modelu; • kliknięcie przycisku logowania przy użyciu selektora CSS; • czy logowanie działa przez sprawdzenie adresu U RL strony, na którą następuje przekierowanie; • czy odnośnik logowania zostaje ukryty, a odnośnik wylogowania zostaje pokazany. Zwróć uwagę na brak w tych testach jakichkolw iek warunków dotyczących oczekiwania. N apisa liśmy te testy tak, jakby to użytkownik korzystał z aplikacji, a kwestie dotyczące ich obsługi pozo stawiliśmy do rozwiązania system om AngularJS i Protractor. A by przeprowadzić te testy, należy wykonać następujące czynności: 1. Jeśli serwer nie jest włączony, wykonaj polecenie node s e r v e r .js w folderze appU n derT est. M oże być konieczne uprzednie wykonanie polecenia npm i n s t a l l w tym folderze. 2. Jeżeli Selenium jeszcze nie działa, wykonaj polecenie webdriver-manager s ta r t. 3. W ykonaj polecenie p ro tra c to r p r o t r a c t o r .c o n f .js w folderze zawierającym plik konfigura cyjny i specyfikacje. Gdy wykonasz te czynności, Protractor uruchom i przeglądarkę Chrom e przy użyciu Selenium, wejdzie na główną stronę naszej lokalnie działającej aplikacji oraz wykona wszystkie zdefiniowane testy. Na koniec powinien wydrukować inform ację, czy testy zakończyły się pomyślnie, czy nie, oraz powód ewentualnej porażki.
252
|
Rozdział 14. Testowanie kompleksowe
Uwagi P o d c z a s p i s a n i a t e s t ó w k o m p l e k s o w y c h d l a a p lik a c j i A n g u l a r J S n a l e ż y m i e ć n a w z g lę d z ie p e w n e w a ż n e k w e s t ie o r a z d o b r z e je st z n a ć i s t o s o w a ć n a j le p s z e p r a k t y k i. P o n iż e j z n a j d u j e s ię i c h o p is :
L ok alizacja dyrektywy ng-app G d y p i s z e s z p r o s t y te st P r o t r a c t o r d la a p lik a c j i A n g u l a r J S i s k ie r u j e s z g o p o d j a k i k o l w i e k a d r e s U R L , p o d k t ó r y m z n a j d u j e s ię a p li k a c j a A n g u l a r J S , P r o t r a c t o r d o m y ś l n i e b ę d z i e s z u k a ł d y r e k t y w y n g - a p p w z n a c z n i k u < b o d y > . K i e d y j ą z n a j d z ie , w ł ą c z y s ię i w y k o n a s w o j e z a d a n ia . A l e j e ś li d y r e k t y w a ta z o s t a n i e z a d e k l a r o w a n a d l a i n n e g o e le m e n t u , n a l e ż y o t y m f a k c ie p o in f o r m o w a ć n a r z ę d z ie P r o tr a c to r. S łu ż y d o te g o o p c ja ro o t E le m e n t k o n f ig u r a c j i P ro tr a c to ra , k t ó r e j p r z e k a z u j e s ię s e le k t o r C S S e le m e n t u z d y r e k t y w ą n g - a p p . N a p r z y k ł a d j e ś li d y r e k t y w a ta je st z d e f i n i o w a n a w p o n i ż s z y s p o s ó b : < d iv c l a s s = " a n g u l a r - a p p " n g -ap p ="m y A p p ">< /d iv > to w k o n f i g u r a c j i P r o t r a c t o r a p o w i n n a z n a le ź ć s ię o p c j a r o o t E l e m e n t o n a s t ę p u j ą c e j t re śc i: ro o tE le m e n t:
". a n g u la r - a p p "
D z i ę k i t e m u P r o t r a c t o r b ę d z ie w ie d z ia ł, że p o t r z e b n y m u e le m e n t n a l e ż y d o k l a s y a n g u la r - a p p . Je żeli d y r e k t y w a n g - a p p je st z d e f i n i o w a n a d la e le m e n t u < b o d y > , o p c ja r o o t E l em ent je st z b ę d n a .
Sondow anie J e śli a p li k a c j a w y k o r z y s t u j e t e c h n i k ę s o n d o w a n i a , p o n i e w a ż p o b i e r a j a k ie ś in f o r m a c j e a lb o w y k o n u j e o b l ic z e n ia c o k i l k a s e k u n d , n i e n a l e ż y u ż y w a ć u s ł u g i A n g u l a r J S $ t im e o u t . P r o t r a c t o r m a p r o b l e m y z o d g a d n i ę c ie m , k i e d y u s ł u g a ta k o ń c z y d z ia ła n ie . Je ż e li w ię c p o t r z e b u j e s z s o n d o w a n i a i c h c e s z je k o m p l e k s o w o p r z e t e s t o w a ć , w z a m i a n u ż y j u s ł u g i $ i n t e r v a l . P r o t r a c t o r d o b r z e z n i ą w s p ó łp r a c u j e .
R ęczny ro zru ch A k t u a l n i e P r o t r a c t o r n i e w s p ó ł p r a c u j e z r ę c z n ie u r u c h a m i a n y m i a p lik a c j a m i A n g u l a r J S . Je śli w ię c p o t r z e b u j e s z k o m p l e k s o w y c h t e s t ó w d l a t a k i c h a p lik a c j i, b ę d z i e s z m u s i a ł p r a c o w a ć z n a r z ę d z i e m W e b D r i v e r ( w y w o ł u j ą c f u n k c j e b r o w s e r . d r i v e r . g e t z a m ia s t b r o w s e r . g e t itd .) o r a z d o d a ć lim it y c z a s u o c z e k iw a n ia , a b y u m o ż l iw ić z a ła d o w a n ie w s z y s t k ie g o , c o je st p o t r z e b n e , p r z e d r o z p o c z ę c i e m te stu . M u s i a ł b y ś z r e z y g n o w a ć z k o r z y ś c i, j a k ie z a p e w n i a P r o t r a c t o r .
W ykonyw anie w przyszłości P o l e c e n i a W e b D r i v e r , k t ó r e p i s z e m y w n a s z y c h t e s t a c h , n ie z w r a c a j ą w a r t o ś c i t y l k o o b ie t n ic e , k t ó r e z o s t a n ą p ó ź n ie j w y k o n a n e w p r z e g lą d a r c e ( a n a w e t w r ó ż n y c h p r z e g lą d a r k a c h ) . Z a t e m f u n k c j a c o n s o l e . l o g n i e z a r e j e s t r u j e ż a d n y c h w a r t o ś c i , p o n i e w a ż n ie b ę d ą o n e d o s t ę p n e w c z a s ie w y k o n y w a n i a k o d u .
D ebugow anie P r o t r a c t o r m a b o g a t y z e sta w n a r z ę d z i d ia g n o s t y c z n y c h , b o w y k o r z y s t u je n a r z ę d z ia d ia g n o s t y c z n e W e b D r i v e r . A b y z d i a g n o z o w a ć w y b r a n y test, w y s t a r c z y d o d a ć p o n i ż s z y w ie r s z k o d u w m ie j s c u , w k t ó r y m c h c e m y r o z p o c z ą ć d e b u g o w a n ie : b r o w s e r .d e b u g g e r ( ) ;
Uwagi
| 253
Wywołanie to m ożna wpisać za dowolną linijką kodu w teście. Później należy uruchom ić testy za pom ocą poniższego polecenia: p r o t r a c t o r debug ś c ie ż k a / d o / c o n f . j s
Powoduje to uruchom ienie debugera Node, który umożliwia wykonywanie kodu między punktam i wstrzymania. W ystarczy nacisnąć klawisz C, a po nim E nter, aby nakazać Protractorow i kontynuow anie w ykonyw ania testów . P ro tracto r będzie w ykonał testy norm aln ie w przeglądarce aż do napotkania instrukcji debugera. W tym m om encie wstrzyma działa nie i będzie czekał na dalsze instrukcje. Jest to prawdziwa aplikacja działająca w przeglądarce, z którą m ożna współpracować i którą m ożna diagnozować w celu dowiedzenia się, co tak n a prawdę „widzi” system wykonawczy narzędzia Protractor. M ożna klikać różne obiekty i zmie niać stan testu, by spowodować jego niepowodzenie. Po zakończeniu diagnostyki m ożna na cisnąć klawisz C, a po nim E nter, żeby kontynuować wykonywanie testu do następnego punktu wstrzymania debugera lub do końca. O statnią rzeczą, którą należy rozważyć, je st organizacja testów w taki sposób, aby łatwo było je opanow ać i aby dało się je w ielokrotnie wykorzystać. W teście przedstaw ionym w poprzednim podrozdziale, służącym do znajdowania elementów na stronie i interakcji z nim i poprzez klikanie, wpisywanie tekstu i tworzenie asercji dotyczących stanu interfejsu użytkownika, użyliśmy kon strukcji element i by. Ale pisząc własne testy, należy utworzyć interfejs A PI pozwalający szybko się zorientować, do czego dany test służy. Dodatkową korzyścią jest to, że przy użyciu tego A PI każdy będzie mógł szybko zbudować zestaw większych i bardziej kompleksowych testów. W celu realizacji tego pomysłu należy wykorzystać pojęcie obiektów stron. W ram ach przykładu przepiszemy test listy drużyn piłkarskich z użyciem obiektów stron zamiast A PI W ebD rivera: / / Plik: r14/routingSpecWithPageObjects.js / / Najlepiej, gdy obiekty stron znajdują się w osobnych plikach, dzięki czemu łatwiej jest ich używać w różnych testach, // ale w tym przypadku dla ułatwienia wrzuciliśmy wszystkie do jednego worka. f u n c t io n T e a m sL istP a g e () t h is . o p e n = f u n c t io n ( ) b r o w s e r . g e t ( '/ ') ;
{ {
}; t h is.g e t T e a m s L ist R o w s = f u n c t io n ( ) { r e t u r n e le m e n t . a ll( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L i s t C t r l. t e a m s ') ) ; }; t h is.g e tR a n k F o rR o w = fu n c t io n (ro w ) { r e t u r n elem ent( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L is t C t r l. t e a m s ') .ro w (r o w ). colum n( ' t e a m .r a n k ') ) ; }; this.ge tN am e Fo rR o w = fu n c t io n (ro w ) { r e t u r n elem ent( b y . r e p e a t e r ( 'D r u ż y n a w t e a m L is t C t r l. t e a m s ') .ro w (r o w ). colum n( ' team.name') ) ; };
254
|
Rozdział 14. Testowanie kompleksowe
t h i s . i s L o g i n L i n k V i s i b l e = f u n c t io n ( )
{
r e t u r n e l e m e n t ( b y . c s s ( '. l o g i n - l i n k ') ) . i s D i s p l a y e d ( ) ; }; t h i s . i s L o g o u t L i n k V i s i b l e = f u n c t io n ( ) { r e t u r n e l e m e n t ( b y . c s s ( '. l o g o u t - l i n k ') ) . i sD i s p l a y e d ( ) ; }; } d e s c r ib e ( 'T e s t t ra s o w a n ia na b a z ie obie któw s t r o n . ',
f u n c t io n ( )
it ( 'P o w i n i e n w y św ie tla ć d ru ż yn y na p ie rw sz e j s t r o n i e . ', v a r te a m sL istP a g e = new T e a m sL is tP a g e ();
{
f u n c t io n ( )
{
team sLi s t P a g e . o p e n ( ) ; e x p e c t(te a m sL istP a g e .g e tT e a m sL i s t R o w s ( ) . c o u n t ( ) ) . t o E q u a l(5 ); e x p e c t(t e a m sL is tP a g e .g e t R a n k F o r R o w (0 ).g e t T e x t ( ) ) . t o E q u a l( ' 1 ') ; e x p e c t(te a m sL istP a g e .g e tN a m e F o rR o w (0 ).g e t T e x t ( ) ) . t o E q u a l( 'H i s z p a n i a '); e x p e c t(t e a m sL is tP a g e .g e t R a n k F o r R o w (4 ).g e tT e x t()) . t o E q u a l( ' 5 ') ; e x p e c t(te a m sL istP a g e .g e tN a m e F o rR o w (4 ).g e tT e x t()) . t o E q u a l( ' U ru g w a j') ;
// sprawdzenie, czy odnośnik logowania jest widoczny, a odnośnik wylogowywania jest ukryty e x p e c t ( t e a m s L is t P a g e . i s L o g in L in k V i s i b l e ( ) ) . t o B e ( t r u e ) ; e x p e c t ( t e a m s L is t P a g e . i s L o g o u t L in k V is i b l e ( ) ) . t o B e ( f a l s e ) ; }); }); U t w o r z y l i ś m y k la s ę J a v a S c r ip t o n a z w ie T e a m s L is t P a g e , u d o s t ę p n ia j ą c ą in t e r f e j s y A P I d o o t w ie r a n i a s t r o n y , p o b i e r a n i a w s z y s t k i c h w i e r s z y i p o b i e r a n i a i n d y w i d u a l n y c h n a z w y i r a n k i n g u d la w y b r a n e g o w ie r s z a . N a s t ę p n i e p r a c u j e m y z e g z e m p l a r z e m k l a s y T e a m s L is t P a g e , d z i ę k i c z e m u test j e s t z n a c z n i e ł a t w ie j s z y w o d b i o r z e n i ż w c z e ś n ie j . P o d o b n y z a b i e g m o ż e m y w y k o n a ć d l a t e s t u s t r o n y lo g o w a n ia . O b i e k t y s t r o n r e p r e z e n t u j ą a b s t r a k c j e o k r e ś la j ą c e s p o s ó b d o s t ę p u d o w y b r a n y c h e le m e n t ó w i i n t e r a k c j i z n i m i w j e d n y m m ie j s c u . D z i ę k i t e m u s ą ła t w e w u ż y c i u i m o d y f i k o w a n i u , p o n i e w a ż n ie t r z e b a w p r o w a d z a ć z m i a n w w ie l u m ie j s c a c h .
Podsumowanie Z a in s t a l o w a liś m y n a r z ę d z ie P r o t r a c t o r i u t w o r z y l iś m y k o n f ig u r a c j ę d la b a r d z o p r o s t e g o te stu k o m p l e k s o w e g o . P ó ź n ie j n a p i s a l i ś m y d w a t e s t y k o m p l e k s o w e . P i e r w s z y s p r a w d z a ł r e n d e r o w a n i e i l o g i k ę p r e z e n t a c y j n ą n a g ł ó w n e j s t r o n i e , a d r u g i t e s t o w a ł p r o c e s l o g o w a n i a w a p lik a c j i. P o n a d t o p r z e d s t a w i l i ś m y lis t ę w a ż n y c h k w e s t ii, o k t ó r y c h n a l e ż y p a m ię t a ć p r z y p i s a n i u t a k i c h te s t ó w .
Podsumowanie
| 255
M a j ą c z e s t a w t e s t ó w k o m p l e k s o w y c h ( a P r o t r a c t o r u m o ż l i w i a z d e f i n i o w a n i e w ie l u t a k i c h z e s t a w ó w d o r ó ż n y c h c e ló w ) , m o ż n a s z y b k o i d o k ł a d n i e s p r a w d z i ć , c z y a p lik a c j a d z i a ł a p r a w i d ł o w o , b e z rę c z n e g o , ż m u d n e g o t e s t o w a n ia k a ż d e g o s k ła d n ik a o s o b n o . W
d u ż y c h a p lik a c j a c h z e s t a w t e
s t ó w k o m p l e k s o w y c h je st n i e o d z o w n y . Z n a s z j u ż p r a w i e w s z y s t k i e a s p e k t y s y s t e m u A n g u l a r J S , w ię c w n a s t ę p n y m r o z d z i a l e p r z e d s t a w i a m y p o d s u m o w a n i e w i a d o m o ś c i , w k t ó r y m z a w a r l i ś m y n a j le p s z e p r a k t y k i i p o r a d y , k t ó r y c h w a r t o p r z e s t r z e g a ć p o d c z a s p i s a n i a a p lik a c j i A n g u l a r J S . P o n a d t o o p i s a l i ś m y k i l k a n a r z ę d z i i a p l i k a c ji, k t ó r e m o g ą u ł a t w ić C i p ra c ę .
256
|
Rozdział 14. Testowanie kompleksowe
_____________________ ROZDZIAŁ 15.
Porady i najlepsze praktyki
W poprzednich rozdziałach nauczyłeś się bardzo wiele na tem at systemu szkieletowego AngularJS i dogłębnie poznałeś prawie wszystkie jego części. W prawdzie jeszcze wiele pozostaje Ci do na uczenia się, ale masz ju ż solidne podstawy do tego, by m óc spojrzeć na ten produkt z szerszej per spektywy. W tym rozdziale chcem y właśnie nie skupiać się na konkretnych funkcjach, tylko na ogólnych kwestiach. Poruszam y więc następujące tematy: • testowanie, • struktura plików i katalogów, • najlepsze praktyki, • budowa aplikacji, • narzędzia i biblioteki. Pokażem y Ci, ja k efektywnie pracow ać z system em szkieletowym AngularJS oraz ja k tworzyć aplikacje, które będą dobrze służyły przez długi czas, nie spow alniając tem pa pracy.
Testowanie Pierwszą i najważniejszą zasadą tworzenia aplikacji sieciowych jest pisanie testów p r z e d rozpo częciem pisania aplikacji, p o d c z a s je j tworzenia oraz p o zakończeniu pracy. Staraliśm y się wpoić Ci te zasady w niniejszej książce przez przedstawianie testów jednostkow ych i kom pleksow ych wszędzie, gdzie się tylko dało.
Programowanie oparte na testach Pisanie testów jednostkowych i specyfikacji z góry jest najlepszą metodą tworzenia dużych i łatwych w obsłudze serwisowej aplikacji AngularJS. Najważniejsze powody, dla których warto tworzyć te sty jednostkow e, opisaliśmy w rozdziale 3. W tym podrozdziale przedstawiamy najlepsze praktyki i zasady, których należy przestrzegać przy pisaniu testów dla swoich projektów.
257
Różnorodność testów N iektórzy twierdzą, że zastosowanie m etodyki T D D (ang. test-driven d ev elop m en t) pozwala cał kowicie pozbyć się błędów z programu. Oczywiście w praktyce metodyka ta doskonale sprawdza się w zapewnianiu jakości szczegółów im plem entacyjnych, ale na większą skalę, w odniesieniu do złożonych aplikacji, testy jednostkow e nie są w stanie wychwycić wszystkich m ożliw ych błędów i usterek. Dlatego tak ważne jest utworzenie zestawu testów jednostkow ych, integracyjnych i sce nariuszowych, dzięki którym m ożna m ieć pewność co do działania swojej aplikacji. T esty jed nostkow e Test jednostkow y jest zwięzły i skoncentrow any na testowaniu tylko jednego elementu (kon trolera, usługi, filtru itd.) i jed n ej funkcji. Za pom ocą tego rodzaju testów (które tw orzy się przy użyciu narzędzia Karm a, opisanego w rozdziale 3.) m ożna sprawdzić, czy jeśli do aplika cji przekaże się prawidłowe dane, to aplikacja ta zwróci poprawne wyniki lub wygeneruje od powiednie skutki uboczne. Jeżeli istnieje taka możliwość, tworzymy im itacje zależności, aby m ieć pewność, że dany składnik jest testowany w izolacji. N a przykład test jednostkow y kon trolera może im itow ać usługę, przy założeniu, że usługa ta działa prawidłowo. W teście jednostkow ym szukamy odpowiedzi na pytania typu: jeśli wszystkie inne składniki działają prawidłowo, to czy ten składnik też zadziała poprawnie? Systematycznie zapewniamy poprawność każdego elem entu, dzięki czemu podczas tropienia trudnego do wykrycia błędu m ożem y bardzo szybko odrzucić te części, których niezaw odność już potwierdziliśmy. T esty in teg racy jn e Testy jednostkow e są bardzo przydatne, szybkie i m ają niew ielkie rozm iary, ale nie są u n i wersalne. Przyjm uje się w nich pewne założenia dotyczące innych składników, ja k kontrolery i usługi. Przyjm ujem y, że działają one w określony sposób lub że zwracają określone in fo r m acje. Bardzo często założenia te są fałszywe. Testy integracyjne (w systemie AngularJS także tworzone przy użyciu narzędzi Karm a i Jasm ine) pozwalają sprawdzić, czy różne części apli kacji są prawidłowo skonfigurowane i połączone. W testach tych m ożna sprawdzić, czy kon troler kom unikuje się z usługą oraz czy usługa wywołuje odpowiednie efekty uboczne i zwraca prawidłowe wartości. Test tego rodzaju napisaliśmy w rozdziale 7., w punkcie „Testy integra cyjn e”, w celu przetestow ania integracji naszego kontrolera z usługą i upew nienia się, czy usługa wykonywała odpowiednie wywołania oraz zwracała właściwe wartości. W teście integracyjnym można przetestować interakcję między kontrolerem i usługą oraz spraw dzić konfigurację interceptorów. Ponadto można badać złożone interakcje przez sprawdzanie, co się dzieje, gdy jed na funkcja zostanie wywołane po innej, albo czy w wyniku sondowania co kilka sekund otrzym ujem y prawidłowe dane. Ale żądania X H R w testach integracyjnych są imitowane, co znaczy, że testy te nie kom unikują się z prawdziwym serwerem. Nawet ładowanie szablonu dla dyrektywy (jak w rozdziale 12.) trzeba imitować. Zatem w teście integracyjnym , choć m ożem y uzyskać pewność, że frontow a logika aplikacji działa prawidłowo, nadal jesteśm y uzależnieni od założenia, że nasz serwer poprawnie odpowiada na nasze żądania.
258
|
Rozdział 15. Porady i najlepsze praktyki
T esty scenariuszow e O s t a t n i r o d z a j t e s t ó w , j a k i p o w i n n i ś m y u t w o r z y ć d l a n a s z e j a p lik a c j i, t o t e s t y k o m p l e k s o w e , c z y l i s c e n a r iu s z o w e . T e o p i s a n e w r o z d z i a l e 14. t e s t y p o l e g a j ą n a u r u c h o m i e n i u p r z e g lą d a r k i, z a ł a d o w a n i u a p lik a c j i i k l i k a n i u w r ó ż n y c h m ie j s c a c h s t r o n y t a k , j a k b y t o r o b i ł u ż y t k o w n i k . S ą t o n a j b a r d z ie j m i a r o d a j n e te sty , k t ó r e p o z w a la j ą n a j le p ie j p r z e t e s t o w a ć d z ia ł a n ie a p lik a c j i. M i m o t o z a l e c a m y o s z c z ę d n e o p e r o w a n i e n i m i i n i e z a s t ę p o w a n ie n i m i w s z y s t k i c h i n n y c h r o d z a j ó w t e s t ó w , p o n ie w a ż : • P o t r z e b a b a r d z o w i e l u t e s t ó w d o p o k r y c i a w s z y s t k i c h p r z y p a d k ó w , k t ó r e m o ż n a p o d z ie l ić i o b s ł u ż y ć z a p o m o c ą t e s t ó w j e d n o s t k o w y c h i in t e g r a c y j n y c h . • P o r a ż k a te stu s c e n a r iu s z o w e g o n ie k o n ie c z n ie d o s ta rc z a in f o r m a c j i p o t r z e b n y c h d o w y k r y c i a ź r ó d ł a p r o b l e m u . W i a d o m o t y lk o , ż e c o ś n ie d z ia ł a t a k , j a k p o w i n n o . D o b r y z e s t a w t e s t ó w s c e n a r i u s z o w y c h je st n ie w ie lk i, s t a b iln y , o b l i c z a l n y i p o k r y w a w i ę k s z o ś ć p u n k t ó w in t e g r a c j i. C e l e m t y c h t e s t ó w je st z a p e w n ie n ie , ż e p r z y n a j m n ie j n a j b a r d z ie j p o d s t a w o w e f u n k c j e a p lik a c j i s ą p o p r a w n i e p o ł ą c z o n e i p r a w i d ł o w o d z ia ła ją . P o w s z e c h n i e a k c e p t o w a n y m t y p o w y m p o d z i a ł e m i l o ś c i o w y m m i ę d z y te t r z y r o d z a j e t e s t ó w je st 7 0 : 2 0 : 10. O z n a c z a to , że 7 0 % t e s t ó w p o w i n n o b y ć t e s t a m i j e d n o s t k o w y m i , 2 0 % p o w i n n o b y ć te s t a m i in t e g r a c y j n y m i, a 1 0 % —
te sta m i k o m p le k s o w y m i. D o te g o n a le ż y d ążyć.
Kiedy wykonywać testy N a p i s a ł e ś ś w i e t n y , s o l i d n y z e s t a w t e s t ó w , k t ó r y w y k r y w a i e li m i n u j e w s z y s t k i e m o ż l i w e b łę d y . I c o d a le j ? K i e d y g o w y k o n y w a ć ? O t o k i l k a w s k a z ó w e k n a t e m a t n a j l e p s z y c h m o m e n t ó w w y k o n y w a n ia testów :
Przy każdym zapisaniu pliku T e s t y j e d n o s t k o w e i in t e g r a c y j n e p o w i n n o s ię w y k o n y w a ć n ie r z a d z ie j n i ż z a k a ż d y m r a z e m , g d y z a p is z e s ię p l ik . T e s t y s c e n a r i u s z o w e m o g ą b y ć t r o c h ę z b y t r o z b u d o w a n e , ale t e s t y j e d n o s t k o w e i in t e g ra c y jn e są
n ie s a m o w ic ie sz y b k ie ( j e ś li n ie , t o z n a c z y ż e s ą ź le n a p i s a n e ! ) .
K a r m a z a w ie r a f u n k c j ę o n a z w i e a u t o W a tc h , k t ó r a u m o ż l i w i a a u t o m a t y c z n e w y k o n y w a n i e t e s t ó w , g d y z m i e n i s ię k o d ź r ó d ł o w y w p l i k u . N i e c o d a le j o p i s u j e m y z i n t e g r o w a n e ś r o d o w i s k o p r o g r a m i s t y c z n e W e b S t o r m , k t ó r e r ó w n i e ż je st z i n t e g r o w a n e z K a r m ą , d z i ę k i c z e m u m o ż e a u t o m a t y c z n i e w y k o n y w a ć t e s t y p o k a ż d y m z a p i s a n i u p l ik u . Z a le t ą t a k ie g o r o z w i ą z a n i a je st to, ż e n a t y c h m i a s t o t r z y m u j e m y in f o r m a c j e z w r o t n e . J e śli w ię c c o ś p ó j d z ie n ie t a k , m o ż n a p o p r o s t u c o fn ą ć z m ia n y i w r ó c ić d o p o p r z e d n ie g o sta n u .
Przed wysłaniem kod u do system u k o n tro li w ersji Je ż e li n i e u ż y w a s z ż a d n e g o s y s t e m u k o n t r o l i w e r s ji, t o n a t y c h m i a s t z a c z n ij . K a ż d y w i ę k s z y p r o j e k t ( w z a s a d z ie w s z y s t k o , c o n i e je st t w o r z o n e d l a z a b a w y ) p o w i n i e n b y ć p r z e c h o w y w a n y w r e p o z y t o r i u m s y s t e m u k o n t r o l i w e r s j i ( n p . G it ,
h ttp ://g it-s c m .c o m / , a lb o M e r c u r i a l —
h ttp ://m ercu rial.selen ic.co m / ). K o d w y s y ł a n y d o t a k ie g o r e p o z y t o r i u m p o w i n i e n b y ć p o p r a w n y . W i ę k s z o ś ć t e g o t y p u s y s t e m ó w u d o s t ę p n i a p u n k t y z a c z e p ie n ia ( n p . G i t H o o k s
— h ttp ://
git-scm .com /book/en /C u stom izin g-G it-G it-H ooks ) u m o ż liw ia j ą c e w y k o n y w a n i e s k r y p t ó w i p o le c e ń w o d p o w i e d z i n a o k r e ś l o n e z d a r z e n ia . N a j le p ie j je st w y k o n y w a ć t e s t y j e d n o s t k o w e , i n t e g r a c y j n e i s c e n a r i u s z o w e p r z e d w y s ł a n i e m l u b z a t w ie r d z e n ie m k o d u , a b y d o b a z y d o d a w a n y b y ł t y lk o s p r a w d z a n y k o d .
Testowanie
| 259
C iągła in te g racja Ciągła integracja to koncepcja polegająca na obserwowaniu zm ian w systemie kontroli wersji i wykonywaniu testów na najnowszym kodzie przy każdym jego wysłaniu do repozytorium. W ten sposób wszelkie awarie i usterki wykrywa się natychm iast zamiast dopiero przy publi kowaniu nowej wersji zawierającej wiele zmian. Ciągła integracja pozwala powiązać problem z konkretną zmianą wprowadzoną w systemie. Zarówno Karma, jak i Protractor dobrze integrują się z większością otwartych systemów do ciągłej integracji, takich jak Jenkins (http://jenkins-ci.org/) i Travis (https://travis-ci.org/), więc jeśli dysponujesz systemem kom pilacji, włącz w nim wyko nywanie wszystkich swoich testów. Wszystkie wymienione czynności wystarczy wykonać raz. Później odpowiednie programy działają w tle bezobsługow o i pom agają w zapewnianiu wysokiej jakości kodu. Zrób więc, co trzeba, na początku, a potem ciesz się dobrymi wynikami.
Struktura projektu Jednym z najważniejszych i najczęściej zadawanych pytań dotyczących projektów AngularJS jest to, ja k powinna wyglądać struktura plików i folderów. Dotyczy to zarówno naszych własnych plików źródłow ych, ja k i im portow anych b ibliotek zew nętrznych, szablonów oraz plików częściow ych. Na początku tego podrozdziału prezentujem y kilka ogólnych porad na tem at najlepszych prak tyk, następnie przedstawiamy najlepsze sposoby budowania struktury katalogów i dołączania ze wnętrznych bibliotek do projektu, a na końcu podsuwamy kilka pomysłów, od czego zacząć pracę nad nowym projektem .
Najlepsze praktyki Treść tego punktu je st krótka i zwięzła, ale poruszone w nim tem aty rozw ijam y w następnych podrozdziałach. P otraktuj go jak o przew odnik albo odświeżenie w iadom ości, które m oże się przydać, gdy nie będziesz wiedział, co dalej robić. • W każdym pliku ogranicz się do jednej definicji kontrolera, usługi, dyrektywy lub filtru. Nie m ieszaj wielu składników w jednym wielkim pliku. • Nie twórz gigantycznych modułów. Lepiej podziel aplikację na m niejsze części. N iech główna aplikacja składa się z wielu m niejszych m odułów nadających się do wielokrotnego użytku. • M oduły i katalogi twórz z myślą o funkcjonalności (autoryzacja, adm inistracja, wyszukiwanie itd.), a nie o typie (kontrolery, usługi, dyrektywy). Dzięki tem u kod będzie łatwiejszy do wie lokrotnego użycia. • Używaj zalecanej składni wykorzystania funkcji modułów (a n g u la r.m o d u le('ja k iśM o d u ł'). ^ c o n tr o ll e r itd.) zamiast innych technik, które można spotkać w internecie i innych źródłach. • Używaj przestrzeni nazw. Dodawaj do nich moduły, kontrolery, usługi, dyrektywy i filtry. Na przy kład utwórz przestrzeń nazw mojafirma-wykres dla dyrektyw, mojProjektAutoryzacja itd. Dzięki tem u nowy program ista bez problem u odróżni Twój kod od rdzennego kodu AngularJS. • Przede wszystkim pisz spójnie. Ta rada nie dotyczy ściśle tylko systemu AngularJS, ale nazy waj pliki, foldery, dyrektywy i kontrolery zawsze według tych sam ych zasad.
260
I
Rozdział 15. Porady i najlepsze praktyki
Struktura katalogów W
t y m p u n k c i e in t e r e s u j e n a s t y l k o s t r u k t u r a k a t a l o g ó w a p l ik a c j i f r o n t o w e j , n ie s e r w e r o w e j .
A p l i k a c j a A n g u l a r J S m o ż e z a w i e r a ć s ię w j e d n y m f o l d e r z e s e r w o w a n y m s t a t y c z n i e z s e r w e r a . N a n a j w y ż s z y m p o z i o m i e o r g a n i z a c y j n y m p r o g r a m t a k i m o ż e z a w ie r a ć n a s t ę p u j ą c e f o ld e r y :
app F o ld e r
a p p s ł u ż y d o p r z e c h o w y w a n i a w s z y s t k i c h p l i k ó w J a v a S c r ip t , k t ó r e s a m i n a p i s z e m y .
D a le j j e s z c z e r o z w i n i e m y tę m y ś l.
tests F o l d e r d o p r z e c h o w y w a n i a t e s t ó w j e d n o s t k o w y c h i e w e n t u a l n ie k o m p l e k s o w y c h t e s t ó w s c e n a r iu s z o w y c h .
d a ta W
f o ld e r z e t y m m o ż n a p r z e c h o w y w a ć w s z y s t k ie n i e d y n a m i c z n e d a n e w y k o r z y s t y w a n e w s p ó l
n ie p r z e z r ó ż n e s k ł a d n i k i a p lik a c ji.
scritps F o ld e r d o p r z e c h o w y w a n ia s k r y p t ó w b u d o w y i i n n y c h czę sto p o t r z e b n y c h s k r y p t ó w .
In n e pliki P o z o s t a ł e p l i k i , k t ó r e n i e m u s z ą b y ć w o s o b n y m k a t a lo g u , n p .
p a c k a g e.jso n , bow er.json i in n e ,
m o ż n a z a p is a ć w f o ld e r z e g ł ó w n y m . Z a n im
p r z e jd z ie m y d o s z c z e g ó ło w e g o o p is u z a w a r to śc i f o ld e r ó w
a p p c z y tests , n a j p i e r w z d e f i
n i u j e m y p e w n e o g ó l n e p o ję c ia :
G rupow anie w edług fu n k cjo n a ln o ści lu b k om p on en tu W
w i e l u p r z y k ł a d a c h i s t a r t o w y c h p r o j e k t a c h A n g u l a r J S s u g e r o w a n a je st t a k a s t r u k t u r a k a
t a lo g ó w , w k t ó r e j u t w o r z o n y je st o s o b n y f o ld e r n a w s z y s t k ie k o n t r o l e r y , o s o b n y n a u s ł u g i itd. Z d o ś w ia d c z e n i a w ie m y , że t a k a b u d o w a s t a n o w i d la r o z r a s t a j ą c e g o się p r o j e k t u p o w a ż n e o g r a n ic z e n ie . D l a t e g o z a l e c a m y g r u p o w a n i e k o d u w f o l d e r a c h w e d ł u g k o m p o n e n t ó w l u b f u n k c j o n a ln o ś c i. N a p r z y k ł a d k a ż d y z k o m p o n e n t ó w lo g o w a n i a , a u t o r y z a c j i i w y s z u k i w a n i a m o ż e m i e ć o s o b n y f o ld e r . W i d o k i i p o d s e k c j e a p lik a c j i r ó w n i e ż m o ż n a g r u p o w a ć w f o ld e r a c h , n p .
a d m in i view , w k t ó r y c h m o g ą b y ć d a ls z e p o d f o l d e r y , j a k se a rc h , list itd . K o m p o n en ty i sekcje ap lik acji P la n u j ą c s t r u k t u r ę a p lik a c j i, c z ę s to w y k o r z y s t u j e m y d w ie k o n c e p c j e . P i e r w s z a t o k o m p o n e n t y , c z y li s t o s o w a n e w a p lik a c j i w id ż e t y w ie lo k r o t n e g o u ż y t k u , k t ó r e n ie s ą z w ią z a n e z ż a d n ą k o n k r e t n ą s t r o n ą a n i s e k c j ą in t e r f e j s u u ż y t k o w n i k a . K o m p o n e n t a m i t a k i m i m o g ą b y ć e le m e n t w y b o r u d a t y a lb o u s łu g a a u t o ry z a c ji —
u ż y w a n e w w i e l u r ó ż n y c h a p lik a c j a c h . W
f o ld e r z e
com p on en ts p o w i n n y z n a j d o w a ć s ię p o d f o l d e r y d o p r z e c h o w y w a n i a r ó ż n y c h s k ł a d n i k ó w : • P o w i ą z a n e u s łu g i, d y r e k t y w y i s p o k r e w n i o n e p lik i. • P o t r z e b n e i n f o r m a c j e i z a s o b y , j a k a r k u s z e s t y l ó w , o b r a z y i in n e , m o ż n a z a p i s a ć w t y m f o ld e r z e b ą d ź o s o b n o , w z a l e ż n o ś c i o d p o t r z e b . • Je śli k o m p o n e n t je st s k o m p l i k o w a n y , k a ż d y f o ld e r m o ż n a p o d z ie l ić n a k o le j n e p o d f o l d e r y .
Struktura projektu
| 261
D r u g a k o n c e p c j a t o s e k c j e a p lik a c j i, k t ó r e o d z w i e r c i e d l a j ą jej s t r u k t u r ę . M o g ą t o b y ć t r a s y , s t r o n y (n p . w y s z u k i w a n i a , z lis t ą e le m e n t ó w , a d m i n i s t r a c y j n a it d .), a n a w e t m n ie j s z e p o d s e k cje s t r o n y ( n p . p u l p it ) . F o l d e r y s e k c j i z r e g u ły : • O d z w ie r c ie d la j ą w id o k i p r z e d s t a w ia n e u ż y t k o w n ik o w i. • Z a w i e r a j ą t y l k o s z a b l o n y H T M L (i C S S ) o r a z p r a c u j ą c y z n i m i k o n t r o l e r . K o m p o n e n t l u b s e k c j a ( a lb o p o d k o m p o n e n t b ą d ź p o d s e k c j a ) m o ż e m i e ć w ł a s n ą d e f in ic j ę m o d u ł u . D e c y z j a , c z y d e f in ic j a t a k a je st w d a n y m p r z y p a d k u p o t r z e b n a , p o w i n n a b y ć p o d j ę t a n a p o d s t a w ie te g o , c z y m o d u ł je st p r z e z n a c z o n y d o w ie lo k r o t n e g o u ż y t k u lu b s e le k t y w n e g o d o ł ą c z a n i a d o r ó ż n y c h a p lik a c j i.
T esty pow inny być lu strzany m od biciem ap lik acji F o ld e r
tests p o w i n i e n z a w ie r a ć g ł ó w n i e k o m p l e k s o w e scenariuszow e testy t w o r z o n e p r z y
u ż y c iu n a r z ę d z ia P ro tra c to r. S t r u k t u r a w e w n ę t rz n a te g o fo ld e r u p o w in n a n a ś la d o w a ć n a s z ą a p lik a c j ę i jej w i d o k i . W
t y m s e n s ie k a ż d y w i d o k i k a ż d a s e k c ja s t r o n y p o d d a w a n e j t e s t o m
w d z ia ła ją c e j n a ż y w o a p lik a c j i m o g ł y b y m ie ć p o d f o l d e r z w ł a s n y m i t e s t a m i. S t r u k t u r a f o ld e r ó w w t y m p r z y p a d k u o d z w i e r c i e d l a ł a b y s t r u k t u r ę a p lik a c j i, a n ie b y ł a d o p a s o w a n a d o is tn ie ją c e j s t r u k t u r y k a t a lo g ó w . N a t o m ia st
testy jed n ostk o w e s ą p r z e c h o w y w a n e w r a z z k o d e m a p lik a c j i w p o d f o l d e r a c h f o l
a p p . D z i ę k i t e m u t e s t y j e d n o s t k o w e w s z y s t k i c h k o n t r o l e r ó w , u s łu g , d y r e k t y w i f i l t r ó w
d e ru
z n a j d u j ą s ię w t y m s a m y m f o ld e r z e c o t e s t o w a n y p r z e z n ie s k ł a d n i k . U ł a t w i a t o z n a j d o w a n ie t e s t ó w i z a r z ą d z a n ie n i m i o r a z p o z w a la n a r z ę d z i o m w d r o ż e n i o w y m w y k l u c z y ć t e s t y z g o t o w e j p a c z k i p ro g ra m o w e j.
K on w en cje nazew nicze N a p o c z ą t e k w a r t o p r z e c z y t a ć p o d r ę c z n i k s t y l i s t y c z n y d o J a v a S c r ip t u f i r m y G o o g l e ( h ttp ://
google-stylegu ide.googlecode.com /svn /tru n k/javascriptgu ide.xm i ) , n a t o m ia s t p o t e m m o ż n a s k o r z y s t a ć z d o d a t k o w y c h z a le c e ń d o t y c z ą c y c h A n g u l a r J S , a k o n k r e t n ie : • N a z w y p l i k ó w p o w i n n y b y ć w y s t a r c z a j ą c o t r e ś c iw e , a b y d a ło s ię p o z n a ć , d o c z e g o d a n y p l i k n a l e ż y i j a k i e g o r o d z a j u o b i e k t A n g u l a r J S r e p r e z e n t u j e . N a p r z y k ł a d j e ś li w a p lik a c j i je st s e k c ja o n a z w ie
a d m in sea r c h , t o f o ld e r m o ż e n a z y w a ć s ię a d m in sec tio n , a z n a j d u j ą c y
s ię w n i m k o n t r o l e r —
ad m in sectio n -co n tro ller.js .
• N a jle p ie j, jeżeli n a z w a p l i k u k o n t r o l e r a k o ń c z y się p r z y r o s t k ie m r o s t k ie m
-controller.js , p l i k u u s ł u g i p r z y
-service.js , p l i k u d y r e k t y w y p r z y r o s t k i e m -directive.js , a f ilt r u p r z y r o s t k ie m -filter.js .
• N a z w y p l ik ó w z a w ie ra ją c y c h te sty p o w i n n y b y ć z a k o ń c z o n e p r z y r o s t k ie m d y r e k t y w a e le m e n t u d o w y b o r u d a t y m o ż e m ie ć n a z w ę p l ik u te st m o ż e z n a j d o w a ć s ię w p l i k u o n a z w i e • W
-test.js . Z a t e m
d a te p ic k er -d ir ec tiv e.js , a jej
d atep icker-d irectiv e_ test.js .
n a z w a c h p l i k ó w le p ie j j e s t u ż y w a ć m a ł y c h lit e r n i ż n o t a c j i w ie lb ł ą d z ie j l u b z a c z y n a ć
k a ż d y w y r a z o d d u ż e j lit e ry . T e r a z z o b a c z m y , j a k te w s z y s t k ie z a s a d y m o ż n a z a s t o s o w a ć w p r a k t y c e . P o n iż e j z n a j d u j e s ię p r z y k ł a d s t r u k t u r y p r o j e k t u p r o s t e j a p lik a c j i t y p u C R U D :
262
|
Rozdział 15. Porady i najlepsze praktyki
. ap p • app.css • app.js • in d ex .h tm l • com p on en ts // k o m p o n e n t y w i e l o k r o t n e g o u ż y t k u • d a tep ick er • d atep icker-d irectiv e.js • d atep icker-d irectiv e_ test.js • au th o riz a tio n • a u t h o r iz a t io n s • a u th orization -serv ice.js • a u th o r iz a tio n -s e r v ic e te s tjs • ui-w idgets • ui-w idgets.js • g rid • g rid .h tm l • grid -d irectiv e.js • g r id -d ir e c tiv e te s t.js • d ia lo g • dialog-service.js • d ia lo g -se rv ic etes t.js • list • list.htm l • list.css • list-controller.js • list-co n tro lle rtes t.js • login • login .htm l • login -con troller.js • search • search .h tm l • search.css • search -con troller.js • search -con troller_ test.js
Struktura projektu
| 263
• d etail • d eta il.h tm l • d e t a il - c o n t r o l le r . j s • d e ta il-c o n tr o lle r te s t.js • a d m in • cre a te • create.h tm l • create-con troller.js • create-con troller_ test.js • u p d a te • u p d ate.h tm l • u p d ate-con troller.js • u p d a te -c o n tr o lle r te s t.js • ven dors // z e w n ę t r z n e z a le ż n o ś c i • u n derscore • jq u e r y • bootstrap • e2 e // t e s t y k o m p l e k s o w e • ru n n er.htm l • login _scen ario.js • lis ts c e n a r io .js • search _ scen ario.js • d etail_ scen ario.js • ad m in • a d m in _ crea te_ sc en a rio .js • a d m in _ u p d a te_ scen a rio.js
Biblioteki zewnętrzne W i e s z ju ż , j a k m o ż e w y g l ą d a ć s t r u k t u r a T w o j e j a p lik a c j i i jej k o d u ź r ó d ł o w e g o , ale c o z z e w n ę t r z n y m i z a l e ż n o ś c i a m i i b i b l io t e k a m i , b e z k t ó r y c h n ie s p o s ó b s o b ie p o r a d z i ć ? Z a l e c a m y u t w o r z e n ie o g ó l n e g o f o ld e r u o n a z w ie k ic h t a k ic h d o d a t k ó w —
vendors w g ł ó w n y m f o ld e r z e a p lik a c j i i p r z e c h o w y w a n ie w n i m w s z y s t
le c z t o t y l k o j e d n a s t r o n a m e d a lu .
A b y u ł a t w i ć s o b i e z a p a n o w a n i e n a d a k t u a li z a c j a m i i r ó ż n y m i w e r s j a m i z e w n ę t r z n y c h z a le ż n o ś c i, z a le c a m y s k o r z y s t a n ie z n a r z ę d z ia t y p u B o w e r
(h ttp ://w w w .bo w er.io /), c z y l i m e n e d ż e r a p a k i e t ó w
d la z a le ż n o ś c i s ie c io w y c h . P r a w ie k a ż d a z e w n ę t r z n a z a le ż n o ś ć u ż y w a n a w p r o j e k c ie A n g u l a r J S je st d o s t ę p n a ta k ż e w p o s t a c i p a k ie t u B o w e r.
264
I
Rozdział 15. Porady i najlepsze praktyki
Instalacja i wdrożenie menedżera Bower do użytku wymaga wykonania tylko trzech prostych czynności: 1. Zainstaluj narzędzie Bower — jest to pakiet N odeJS, więc wystarczy wykonać polecenie npm in s t a ll bower. 2. Utwórz listę swoich zależności w pliku definicji pakietów Bower (najczęściej o nazwie bow er.json). W pliku tym należy określić wersje bibliotek używanych w projekcie. 3. W ykonaj polecenie bower in sta l l (albo bower update) w dowolnym projekcie, aby pobrać naj nowsze zależności do folderu tego projektu i włączyć automatyczną obsługę wersji programów. W czym pomaga narzędzie Bower: • Zwalnia programistę z konieczności wprowadzania zewnętrznych bibliotek do bazy kodu. • Ułatwia zarządzanie aktualizacjami zewnętrznych bibliotek, które sprowadza się do zmiany num eru w jednym pliku JSON. • Ułatwia każdemu now emu program iście przystępującemu do projektu sprawdzenie, jakie zewnętrzne biblioteki są używane. Krótko mówiąc, Bower ułatwia integrację zewnętrznych bibliotek z projektem. System AngularJS też jest dostępny w postaci pakietu Bower, więc koniecznie go wypróbuj w swojej następnej aplikacji!
Punkt początkowy Przedstawiliśmy nasze zdanie na tem at tego, ja k powinna wyglądać struktura plików i katalogów projektu AngularJS i ja k powinno się nazywać pliki w takim projekcie, oraz opisaliśmy kilka do brych praktyk. Ale nie musisz tego wszystkiego robić samodzielnie od zera. W spaniała społecz ność skupiona wokół projektu AngularJS stworzyła wiele przydatnych narzędzi i bibliotek, które m ożna wykorzystać jako bazę do tworzenia własnego projektu. Poniżej prezentujem y kilka p o mysłów, od czego zacząć pracę nad nowym projektem : Y eo m an Yeoman to menadżer zadań automatyzujący wiele rutynowych czynności, których nie da się unik nąć w żadnym projekcie. Nie jest to narzędzie przeznaczone wyłącznie dla systemu AngularJS, ale ma dla niego wtyczkę. Sam system AngularJS uwalnia programistę od konieczności pisania sza blonowego kodu, lecz nie napisze za nas tras, nie doda kodu H TM L i kontrolera, nie utworzy szkieletu dla testu jednostkowego itd. Natomiast Yeoman idzie o krok dalej i automatyzuje właśnie te czynności. Wystarczy napisać polecenie w stylu yo angul a r:ro u te moj aNowaTrasa, aby wygene rować plik route.htm l, dodać szkielet kontrolera i testu, dodać kod JavaScript do pliku index.htm l itd. Istnieje kilka generatorów AngularJS, różniących się składnią i strukturą folderów, więc jest duża szansa, że znajdziesz coś odpowiadającego Twoim potrzebom. Yeoman zapewnia także skrypty kompilacji i zadania Grunt, które mogą być potrzebne w średnich i dużych przedsięwzięciach. P ro jek ty początkow e A n g u larJS Jest wiele projektów początkowych w AngularJS, które zapewniają podstawową strukturę kata logów, szkielet aplikacji i skrypty kom pilacyjne. Do najczęściej używanych i najlepiej znanych zaliczają się n g -b o ile rp la te , i angular-seed. O ba dostarczają podstawową strukturę i zadania Grunt, które są potrzebne do rozpoczęcia pracy.
Struktura projektu
| 265
M ean .io A jeśli działasz w branży rozwiązań kom pleksowych i nie masz nic przeciwko wykorzystaniu N odeJS jak o serw era, to m oże zainteresow ać Cię p ro jek t M ean .IO ( h ttp ://w w w .m e a n .io /). Zdobywający ostatnio dużą popularność akronim M EA N oznacza M ongoD B, Express, AngularJS i NodeJS. M ean.IO to gotowy szkielet projektu aplikacji wyposażony w odpowiednią strukturę folderów, skrypty kom pilacyjne itd. C hoć narzuca pewien rodzaj budowy aplikacji i paradygmat pracy, to są one bardzo dobrze przemyślane i warto dać im szansę.
Budowanie projektu Opisaliśm y strukturę folderów projektu, konw encje nazewnicze, a nawet kwestie dotyczące uru cham iania projektów A ngularJS. N iektóre m echanizm y rozruchow e są zaopatrzone w skrypty budow y i zadania G runt, ale i tak należy wiedzieć, na czym polega budow a projektu AngularJS oraz co jest najważniejsze przy tworzeniu takiego projektu.
Grunt G runt to w zasadzie standardowe narzędzie, jeśli chodzi o skrypty do budow y projektów Java Script. Ten napisany przy użyciu JavaScriptu i N odeJS m echanizm do urucham iania zadań m a wiele wtyczek pozwalających zautomatyzować różne etapy budowy projektu, a konkretnie: • łączenie plików; • kopiowanie i przenoszenie plików; • zm ienianie nazw plików; • m inim alizację plików CSS; • m inim alizację plików JavaScript; • wykonywanie testów i skryptów powłoki. Praca z narzędziem Grunt polega na utworzeniu pliku JavaScript im portującego wszystkie p o trzebne wtyczki, a następnie na zdefiniowaniu za pom ocą JavaScriptu własnych zadań i czynności do wykonania dla określonych zadań narzędzia Grunt. Na przykład m ożna zdefiniować zadanie budowy wykonujące następujące czynności: • uruchom ienie testów K arm a i P rotractor w celu zweryfikowania stanu aplikacji; • połączenie wielu plików JavaScript w jeden; • uruchom ienie kom pilatora JavaScript w celu usunięcia spacji i zredukowania rozm iaru kodu JavaScript; • skompilowanie kodu CSS do m niejszej postaci; • przeniesienie plików H TM L, JavaScript i CSS do osobnego katalogu w ramach procesu wdrażania. Innym zadaniem m oże być włączanie wszystkich testów jednostkow ych i integracyjnych wyko rzystujących tylko Karm ę itd. Narzędzie Grunt m a wiele opcji konfiguracyjnych, dzięki którym za jego pom ocą m ożna stworzyć taki proces, jakiego się potrzebuje.
266
|
Rozdział 15. Porady i najlepsze praktyki
Serwowanie pojedynczego pliku JavaScript U ż y w a j ą c n a r z ę d z i a G r u n t (a t a k ż e k a ż d e g o i n n e g o n a r z ę d z ia t e g o t y p u ) , m u s i m y p a m ię t a ć o k i l k u r z e c z a c h i z a d b a ć o to, b y p o d c z a s p r o c e s u b u d o w y z o s t a ł y w y k o n a n e p e w n e c z y n n o ś c i. P r z e d e w s z y s t k i m n a l e ż y z w r ó c i ć u w a g ę n a p l i k i J a v a S c r ip t (a k o n k r e t n ie n a i c h lic z b ę ) d o d a w a n e d o p l i k u
index.htm l ( lu b j a k i e g o k o lw i e k in n e g o ) . P o d c z a s p r a c y n a d a p lik a c j ą A n g u l a r J S lep ie j je st p o s ł u g iw a ć s ię w i e l o m a p l i k a m i i f o ld e r a m i, p o n i e w a ż u ł a t w i a t o z a p a n o w a n i e n a d p r o j e k t e m i j e g o d e b u g o w a n ie . A l e d o ł ą c z a n i e 2 5 0 p l i k ó w J a v a S c r ip t d o a p lik a c j i p r o d u k c y j n e j to i d e a l n y p r z e p i s n a p o w o l n ą s t r o n ę in t e r n e t o w ą . P r z e g l ą d a r k i m a j ą w b u d o w a n e o g r a n i c z e n i a c o d o l i c z b y r ó w n o c z e ś n i e w y s y ł a n y c h ż ą d a ń GET d o j e d n e j d o m e n y . P o w i e d z m y n p . , ż e p r z e g l ą d a r k a m o ż e p o b r a ć n a r a z t y l k o 8 p l i k ó w , a w ię c 2 5 0 p l i k ó w z o s t a n ie p o b r a n y c h w p a r t i a c h p o 8, c o p r z e c ię t n e m u u ż y t k o w n i k o w i k o m p u t e r a z a j m ie w ie c z n o ś ć . Z a l e c a n y m s p o s o b e m s e r w o w a n i a p l i k ó w J a v a S c r i p t w a p l i k a c j a c h p r o d u k c y j n y c h j e s t ł ą c z e n ie i c h w j e d e n d u ż y p l i k . D z i ę k i t e m u g d y p r z e g l ą d a r k a z a ż ą d a p l i k ó w J a v a S c r ip t a p lik a c j i, z o s t a n ą o n e p r z e s ł a n e w j e d n y m ż ą d a n i u , a n i e w 2 5 0 c z y c h o ć b y 50. Je ż e li a p lik a c j a je st s e r w o w a n a w in t r a n e c ie , t o w s z y s t k ie p l i k i J a v a S c r ip t ( w lic z a j ą c z a le ż n o ś c i z e w n ę t r z n e ) b ę d ą s e r w o w a n e t y l k o p r z e z tę a p lik a c j ę , w ię c m o ż n a p o k u s i ć s ię o d o ł ą c z e n i e d o j e d n e g o w i e l k i e g o p l i k u t a k ż e z e w n ę t r z n y c h b ib lio t e k . Z d r u g ie j s t r o n y z a l e ż n o ś c i z e w n ę t r z n e c z ę s t o d o ł ą c z a s ię z s ie c i d o s t a r c z a n ia t r e ś c i ( a n g .
con ten t
delivery netw ork — C D N ) z a m ia s t z w ł a s n y c h z a s o b ó w . D z i ę k i t e m u je ś li u ż y t k o w n i k j u ż w c z e ś n ie j p o b r a ł B o o t S t r a p z C D N p o d c z a s p r z e g l ą d a n i a in n e j w i t r y n y in t e r n e t o w e j , to p o w e j ś c iu n a n a s z ą s t r o n ę n ie b ę d z ie m u s i a ł p o b ie r a ć te g o s k r y p t u p o r a z d r u g i, p o n ie w a ż p r z e g lą d a r k a z a p is z e g o w s w o jej p a m i ę c i p o d r ę c z n e j . D w i e ś w ie t n e s ie c i C D N d la b ib l io t e k J a v a S c r ip t t o G o o g l e H o s t e d L ib r a r ie s
(h ttp s://d ev elop ers.g o o g le.co m /sp eed /libra ries/) i C D N J S (h ttp ://cd n js.co m /).W n i c h p o w i n n o s ię u d a ć z n a le ź ć w i ę k s z o ś ć b i b li o t e k , k t ó r e b ę d ą C i p o t r z e b n e . P r z y o k a z j i ł ą c z y ć m o ż n a n i e t y lk o p l i k i J a v a S c r ip t , ale i C S S . N a l e ż y d ą ż y ć d o te g o , a b y a p lik a c j a p o d c z a s ł a d o w a n i a n i e w y k o n y w a ł a w ię c e j n i ż 3 - 5 r ó w n o c z e s n y c h ż ą d a ń . D z i ę k i t e m u u n i k a się z b lo k o w a n ia p r z e g lą d a r k i i s z e r e g o w a n ia p r z e z n ią ż ą d a ń .
Minimalizacja P o d c z a s g d y łą c z e n ie p l i k ó w J a v a S c r ip t w j e d e n d u ż y p l i k r e d u k u j e lic z b ę ż ą d a ń w y s y ł a n y c h p r z e z p r z e g lą d a r k ę d o s e r w e r a , n i e m a o n o w p ł y w u n a o g ó l n ą i l o ś ć d a n y c h , j a k ą t r z e b a p r z e s ł a ć p r z e z in t e r n e t . A b y a p lik a c j a b y ł a s z y b k a j a k b ł y s k a w i c a , n a l e ż y te ż z a d b a ć o tę d r u g ą k w e s tię . P l i k i J a v a S c r ip t i C S S p o w i n n o się łą c z y ć w p o j e d y n c z e d u ż e p lik i, k t ó r e z k o le i p o w i n n o się p o d d a w a ć p r o c e s o w i m i n i m a l iz a c j i w t a k ic h n a r z ę d z i a c h j a k n p . U g l i f y J S c z y C lo s u r e C o m p ile r f ir m y G o o g le
(https://github.com /m ishoo/U glifyJS)
(https://developers.google.com /closu re/com piler/). T e d w a k o m
p il a t o r y J a v a S c r i p t u p r z e g lą d a j ą p l i k J a v a S c r ip t i u s u w a j ą z n i e g o w s z y s t k ie n ie p o t r z e b n e r z e c z y , t a k ie j a k s p a c je i k o m e n t a r z e , w t e n s p o s ó b z m n ie j s z a j ą c j e g o o b j ę t o ś ć . P o n a d t o k o m p i l a t o r m o ż e te ż s k r a c a ć n a z w y z m i e n n y c h i f u n k c j i, p o n i e w a ż d la p r z e g lą d a r k i n ie m a j ą o n e ż a d n e g o z n a c z e n ia .
Budowanie projektu
| 267
P r z y p o m i n a m y , że j e ś li p l a n u j e s z m i n i m a l i z o w a ć p l i k i, m u s i s z s t o s o w a ć b e z p i e c z n y s t y l w s t r z y k i w a n i a z a le ż n o ś c i. J e ż e li o t y m z a p o m n i s z , a p lik a c j a m o ż e p r z e s t a ć d z ia ła ć . P o n a d t o m o ż n a u ż y ć o t w a r t e j b i b l i o t e k i n g - a n n o t a t e ( h ttp s://g ith u b .com /o lo v /n g -an n o ta te ) , k o n w e r t u j ą c e j k o d n ie n a d a j ą c y s ię d o m i n i m a l i z a c j i n a k o d n a d a j ą c y s ię d o te g o . P o d o b n i e d z ia ła j ą k o m p i l a t o r y C S S , tzn . t a k ż e u s u w a j ą s p a c je i k o m e n t a r z e o r a z m o g ą p r z e p is y w a ć a r k u s z e s t y l ó w w b a r d z ie j e f e k t y w n y s p o s ó b . M i n i m a l i z a c j a p o w i n n a b y ć w y k o n y w a n a w k a ż d e j a p lik a c j i.
Zadanie ng-templates Z a d a n i e n a r z ę d z i a G r u n t d o u ż y t k u w in t e r n e c i e n a z y w a s ię n g - t e m p l a t e s
( h ttp s://g ith u b .c o m /
er icclem m o n s/g ru n t-a n g u la r-tem p la tes ). U m o ż l i w i a o n o u p r z e d n i e z a ł a d o w a n i e w s z y s t k i c h p o t r z e b n y c h w a p lik a c j i s z a b l o n ó w H T M L , z a m i a s t w y k o n y w a ć ż ą d a n i a X H R w c e lu i c h p o b r a n i a d o p i e r o w t e d y , g d y s ą p o t r z e b n e . Z a d a n i e n g - t e m p l a t e s w c z y t u j e w s z y s t k ie p l i k i H T M L i g e n e r u j e p l i k A n g u l a r J S J a v a S c r ip t z a w ie r a j ą c y c a ły k o d H T M L w b u f o r z e t e m p la t e C a c h e p o d c z a s ł a d o w a n i a a p lik a c j i. Z a d a n i e b u d o w y n g - t e m p l a t e s d o s k o n a l e s p r a w d z a s ię w p r z y p a d k u n ie w ie lk ie j l i c z b y s z a b l o n ó w lu b k ie d y u z a s a d n io n e je st w c z y t a n ie z g ó r y w s z y s t k ic h s z a b lo n ó w , a b y p r z y s p ie s z y ć d z ia ła n ie p r o g r a m u . A l e j e ś li s z a b l o n ó w je st d u ż o , m o ż n a w y b r a ć d o z b u f o r o w a n i a t y l k o n a j c z ę ś c ie j u ż y w a n e , a p o z o st a łe ła d o w a ć a s y n c h r o n ic z n ie .
Najlepsze praktyki O m ó w i l i ś m y j u ż t e m a t y s t r u k t u r y f o l d e r ó w i m e t o d b u d o w y p r o j e k t ó w , w ię c t e r a z z a j m i e m y się n a j l e p s z y m i p r a k t y k a m i t w o r z e n i a a p lik a c j i A n g u l a r J S . S ą t o r z e c z y , k t ó r e n a l e ż y r o b i ć l u b k t ó r y c h n a l e ż y u n i k a ć j a k o g n i a , a b y n i e o c z e k i w a n i e n i e z n a l e ź ć s ię w n i e z w y k l e t r u d n e j s y t u a c j i. N i e k t ó r e p o r a d y w y n i k a j ą p o p r o s t u z e z d r o w e g o r o z s ą d k u , a i n n e s ą b a r d z o s p e c y f i c z n e , ale w s z y s t k ie w a r t o p a m ię t a ć .
Uwagi ogólne P o n i ż e j p r z e d s t a w i a m y k i l k a o g ó l n y c h p o r a d , o k t ó r y c h w a r t o p a m i ę t a ć p r z y p i s a n i u a p lik a c j i A n g u la r J S : • M a ł e p l i k i s ą le p s z e o d d u ż y c h . O w ie le ła t w ie j n a d n i m i z a p a n o w a ć , z n a le ź ć w n i c h e w e n t u a ln e b ł ę d y i je z r o z u m i e ć . P r z y j m u j e się , ż e d u ż y p l i k t o t a k i, k t ó r y z a w i e r a w ię c e j n i ż 1 0 0 w ie r s z y k o d u . • U ż y w a j f u n k c j i s e t T im e o u t s y s t e m u A n g u l a r J S , k t ó r a je st u s ł u g ą $ t im e o u t , i f u n k c j i s e t I n t e r v a l s y s t e m u A n g u l a r J S , k t ó r a je st u s ł u g ą $ i n t e r v a l . W r a z ie p o t r z e b y ła t w o je i m i t o w a ć w t e s t a c h j e d n o s t k o w y c h i n i e t r z e b a c z e k a ć n a z a k o ń c z e n i u o d s t ę p u c z a s o w e g o , a b y w y k o n a ć test. M o ż n a n a w e t u s t a w ić lic z b ę w y w o ł a ń f u n k c j i in t e r w a ł o w e j w s w o i c h te s t a c h . D l a t e g o u ż y w a j w e r s j i A n g u l a r J S t y c h f u n k c j i.
268
|
Rozdział 15. Porady i najlepsze praktyki
• J e śli w k o n t r o l e r z e l u b d y r e k t y w ie w y k o r z y s t a n a z o s t a n ie u s ł u g a $ t im e o u t l u b $ i n t e r v a l , to n a l e ż y j ą s k a s o w a ć b ą d ź a n u l o w a ć p o jej z n is z c z e n iu , ż e b y n ie p o t r z e b n ie n ie d z ia ł a ła w tle. • J e ż e li d o d a j e s z p r o c e d u r y n a s ł u c h o w e p o z a z a s i ę g i e m A n g u l a r J S , z a d b a j o i c h p r a w i d ł o w e s k a s o w a n i e . S y s t e m A n g u l a r J S u t r z y m u j e w ł a s n e z a k r e s y i p r o c e d u r y n a s ł u c h o w e , ale w s z y s t k o , c o p r o g r a m i s t a d o d a s a m o d z i e l n ie , m o ż e w y m a g a ć s p e c j a l n e g o p o t r a k t o w a n ia . C z y n n o ś c i z t y m z w ią z a n e m o ż n a w y k o n a ć p o d c z a s n is z c z e n ia z a k r e s u p r z e z d o d a n ie p r o c e d u r y n a s łu c h o w e j $ s c o p e . $ o n ( '$ d e s t r o y ',
fu n c t io n ()
{}).
• S ta r a j s ię u n i k a ć g ł ę b o k i c h o b s e r w a c j i ( $ s c o p e . $ w a t c h ( ) z t r z e c im a r g u m e n t e m u s t a w i o n y m n a t r u e ) . J e st t o c z a s o c h ł o n n a c z y n n o ś ć , k t ó r e j n a d u ż y w a n i e m o ż e p o g o r s z y ć w y d a j n o ś ć a p lik a c j i . Z a m i a s t t e g o le p ie j j e s t u t w o r z y ć p r o s t ą z m i e n n ą l o g i c z n ą s y g n a l i z u j ą c ą z m i a n y o b i e k t u i o b s e r w o w a ć tę z m ie n n ą . • S ta r a j s ię p o s t ę p o w a ć z g o d n i e z p r z y j ę t y m w A n g u l a r J S m o d e l o w y m p a r a d y g m a t e m p r o g r a m o w a n i a . M o d e l i d a n e p o w i n n y d e c y d o w a ć o s t a n i e i n t e r f e j s u u ż y t k o w n i k a , a j e ś li t r z e b a z a k t u a l i z o w a ć t e n in t e r f e j s , w y s t a r c z y t y l k o z a k t u a l i z o w a ć m o d e l . W
r e a k c j i n a t o in t e r f e j s
u ż y t k o w n i k a p o w i n i e n z a k t u a l i z o w a ć s ię a u t o m a t y c z n ie . • Je ż e li c h c e s z w y k o n a ć p e w n e z a d a n i a z a k a ż d y m r a z e m , g d y s e r w e r z w r ó c i b ł ą d a u t o r y z a c j i a lb o 4 0 4 , u ż y w a j i n t e r c e p t o r ó w H T T P . N i e c h t y lk o u s ł u g i i k o n t r o l e r y o b s ł u g u j ą k o n k r e t n e r o d z a j e b łę d ó w .
Uwagi dotyczące usług U s ł u g i m o ż n a s t o s o w a ć d o t w o r z e n ia w s p ó l n y c h in t e r f e j s ó w A P I i m a g a z y n ó w n a d a n e d la całej a p lik a c j i. O t o k i l k a w s k a z ó w e k , j a k i c h n a j le p ie j u ż y w a ć : • U s ł u g i s ą s i n g l e t o n a m i . W y k o r z y s t a j t e n f a k t d o u t w o r z e n i a in t e r f e j s u A P I , m a g a z y n u n a d a n e a l b o b u f o r a . W e w s z y s t k i c h t y c h z a s t o s o w a n i a c h u s ł u g i s p r a w d z a j ą s ię d o s k o n a l e . • J e śli c h c e s z u t r z y m y w a ć w a p li k a c j i w s p ó l n y s t a n , t o r o z w a ż m o ż l i w o ś ć u t w o r z e n i a u s łu g i. • U s ł u g i , f a b r y k i i d o s t a w c y n ie r ó ż n i ą s ię m i ę d z y s o b ą p o d w z g l ę d e m w y d a j n o ś c i. W s z y s t k i e te k o n s t r u k c j e im p l e m e n t u j e s ię t a k s a m o , w ię c w y b i e r z tę, k t ó r a n a j b a r d z ie j o d p o w i a d a T w o j e m u s t y l o w i p r o g r a m o w a n i a i T w o i m p o t r z e b o m , a p o t e m k o n s e k w e n t n i e jej u ż y w a j . • U s ł u g i t o j e d y n e m ie j s c e , w k t ó r y m m o ż n a d o d a w a ć p r o c e d u r y n a s ł u c h o w e d l a $ r o o t S c o p e . Jest t o z w i ą z a n e z t y m , że u s ł u g i n i e m a j ą w ł a s n e g o z a k r e s u . • U s ł u g i z ł o ż o n e z w i e l u w a r s t w s ą ś w ie t n e . Z a m i a s t t w o r z y ć j e d n ą g i g a n t y c z n ą u s ł u g ę r o b ią c ą w s z y s t k o , le p ie j je st p o d z i e l i ć ją n a c z ę ś c i, a p ó ź n ie j u t w o r z y ć j e d n ą w i ę k s z ą u s ł u g ę u ż y w a j ą c ą w s z y s t k i c h t y c h e l e m e n t ó w w z a le ż n o ś c i o d p o t r z e b y . • W y w o ł a n i a X H R w u s ł u g a c h p o w i n n o s ię w y k o n y w a ć z a p o m o c ą u s ł u g i $ h t t p . D z i ę k i t e m u w s z y s t k i e w y w o ł a n i a A P I b ę d ą w j e d n y m m ie j s c u , w k t ó r y m m o ż n a b ę d z ie z m i e n i a ć a d r e s y U R L . D l a s p ó j n o ś c i u s ł u g a p o w i n n a z w r a c a ć o b ie t n ic ę . • In t e g r a c j i p r o g r a m u z z e w n ę t r z n y m i b i b l i o t e k a m i u s ł u g o w y m i ( n i e d o t y c z ą c y m i in t e r f e j s u u ż y t k o w n i k a , j a k n p . S o c k e t I O ) p o w i n n o s ię d o k o n y w a ć p r z y u ż y c i u u s łu g . U m o ż l i w i a to in t e g ro w a n ie i z a m ie n ia n ie d o d a t k ó w w d o w o ln y m m o m e n c ie o ra z ic h im it o w a n ie w te sta c h je d n o stk o w y c h .
Najlepsze praktyki
| 269
Kontrolery Poniżej znajduje się kilka porad na tem at tw orzenia kontrolerów: • W pracy z kontroleram i lepiej jest używać nowszej składni lub definiować zm ienne i funkcje na obiekcie t h i s k ontrolera. To znaczy, że wszędzie, gdzie je st to możliwe, należy stosować składnię c o n t r o l l e r A s i unikać $ s c o p e . Nowa składnia jest bardziej zwięzła i zrozumiała. • Uważaj na zm ienną t h i s . N ajlepiej przypisać ją do lokalnej zm iennej (np. s e l f ) i posługiwać się tą zmienną. • Kontrolery nie powinny odnosić się do struktury D O M . Nie używaj biblioteki jQ uery bezpo średnio w kontrolerach. • K ontroler powinien zawierać tylko logikę prezentacyjną dotyczącą pobierania odpowiednich danych, ich prezentow ania na ekranie oraz obsługi in terak cji z użytkownikam i. I większość z tego powinna być przekazywana w razie m ożliwości do usługi. • Do obiektu t h i s kontrolera powinno się wstawiać tylko te zm ienne i funkcje, które są p o trzebne w kodzie H TM L. Jeśli coś nie jest potrzebne w kodzie H TM L, to powinno być zdefi niowane jako zm ienna lokalna kontrolera. W yjątkiem są oczywiście funkcje, które trzeba przetestować w testach jednostkowych. • Staraj się używać jednorazowego wiązania AngularJS (składnia : : wprowadzona w AngularJS 1.3 lub alternatywnie m ożna korzystać z otwartego rozwiązania b in d o n c e — h ttp s://g ith u b .co m / P a sv a z /b in d o n ce), które opisaliśm y w rozdziale 2. Dzięki tem u będziesz m ógł zaktualizować interfejs użytkownika raz, gdy pojawią się dane, i nie przejmować się więcej danym elementem. W pływ tego na wydajność aplikacji może być bardzo korzystny. • Jeżeli kontroler jest przeznaczony dla konkretnej trasy dostępnej przez adres U RL, to powi nien ładować wszystkie potrzebne dane wtedy, gdy jest tworzony jego egzemplarz. • Jeśli kontroler zapisuje stan całej aplikacji, to powinien to robić w usłudze, a nie w $ r o o t S c o p e . Nigdy nie używaj do tego celu $ r o o t S c o p e . • Kontrolery ($ b r o a d c a s t lub $ e m it ) mogą emitować zdarzenia we własnym zakresie albo wstrzy kiwać $ r o o t S c o p e i wywoływać zdarzenia na nim . Ale lepiej, żeby kontroler nie dodawał pro cedur nasłuchow ych dla $ r o o t S c o p e , ponieważ kontroler wraz zakresem może zostać znisz czony, a $ r o o t S c o p e jest dostępny w całej aplikacji wraz ze swoimi procedurami nasłuchowymi, które będą wyzwalane nawet wtedy, gdy kontroler będzie niedostępny.
Dyrektywy Dyrektywy należą do najbardziej przydatnych składników systemu AngularJS. Poniżej przedsta wiamy kilka porad, ja k je najlepiej wykorzystać: • Jeśli chcesz użyć zewnętrznego kom ponentu interfejsu użytkownika, dodaj go do aplikacji jako dyrektywę AngularJS. • Jeżeli potrzebujesz kom ponentów w ielokrotnego użytku, staraj się izolow ać zakres, aby z a pobiec zm odyfikow aniu zakresu nadrzędnego i wyelim inow ać wszelkie zależności od jego składników .
270
|
Rozdział 15. Porady i najlepsze praktyki
• Jeśli reagujesz na zewnętrzne zdarzenia lub wywołania zwrotne i aktualizujesz model AngularJS, nie zapomnij wywołać funkcji $sco p e().$ a p p ly () — w przeciwnym razie interfejs użytkownika Twojej aplikacji nie będzie aktualizowany w odpowiednim czasie. • Jeżeli dodasz procedurę nasłuchu zdarzeń dla elementów spoza dyrektywy bądź użyjesz funkcji sondującej, pamiętaj, by je usunąć przy niszczeniu dyrektywy. • Jeśli tworzysz nowy zakres potom ny lub zakres izolowany, wykonuj operacje porządkowe na sygnał zdarzenia $scope $destroy. Ale jeśli dziedziczysz zakres nadrzędny, lepiej wykonać te operacje na sygnał zdarzenia $element $destroy. • Jeżeli kontroler musi udostępniać stan dyrektywie, to powinien: • Przekazać ten stan za pom ocą atrybutów H TM L (i izolowanego zakresu), jeśli kom ponent nie jest przypisany wyłącznie do jednej aplikacji i może być używany wielokrotnie. • Przekazywać tan stan za pom ocą usługi, jeśli kom ponent je st przypisany wyłącznie do jed n ej aplikacji. • Jeśli chcesz wykonywać pewną operację zawsze, gdy zm ieni się obiekt, przekaż go za pom ocą wiązania = na zakresie i dodaj $scope.$watch. • N igdy n ie p rz y p isu j p o n o w n ie referencji obiektu przekazanego przez zakres. Innym i słowy, jeśli obiekt s c o p e .fir s tO b je c t jest przekazywany przez wiązanie =, to w dyrektywie nie należy ustawiać ani nadpisywać wartości scope. f i rstObj ect. Modyfikacja klucza w obiekcie f i rstObj e ct jest dozwolona, ale ponowne przypisanie do zmiennej fir s tO b je c t nie powinno m ieć miejsca. • Do kontrolow ania sposobu i czasu aktualizowania modeli oraz ewentualnie w celu polepsze nia wydajności aplikacji używaj dyrektywy ngModelOptions. Dyrektywę tę m ożna zdefiniować dla elem entu najwyższego poziom u, aby była stosow ana wszędzie, zam iast deklarow ać ją wielokrotnie w różnych m iejscach.
Filtry Filtry są doskonałym narzędziem do ostatecznego form atow ania albo m odyfikow ania danych. O to dwie porady na tem at ich stosowania: • Każdy filtr użyty w kodzie H T M L jest wykonywany w każdym cyklu obliczeniow ym . Jeśli wiadomo, że dane nie będą się często zm ieniać, to bardziej efektywnym rozwiązaniem może być zastosowanie filtru bezpośrednio w kontrolerze, ja k pokazaliśm y w rozdziale 8. • Filtry powinny być bardzo szybkie, ponieważ mogą być wykonywane wielokrotnie w trakcie działania aplikacji. Dlatego nie powinno się w nich wykonywać żadnych intensywnych obli czeń ani modyfikować struktury D O M .
Narzędzia i biblioteki W ostatnim podrozdziale przedstawiamy kilka narzędzi i bibliotek ułatwiających pracę program i ście korzystającem u z systemu AngularJS. N iektóre z nich to narzędzia program istyczne, a inne to biblioteki kom ponentów i m oduły m ogące przydać się w projekcie.
Narzędzia i biblioteki
| 271
Batarang K a ż d y p r o g r a m i s t a p o s ł u g u j ą c y s ię s y s t e m e m A n g u l a r J S p o w i n i e n w y p o s a ż y ć s ię w r o z s z e r z e n ie p r z e g lą d a r k i C h r o m e o n a z w ie B a t a r a n g ( https://chrom e.google.com /w ebstore/detail/angularjs-batarang/
ighdm ehidhipcm cojjgtioacoafjm pfk?hl=en ) , k t ó r e u ł a t w i a d e b u g o w a n i e i o g ó l n i e p r a c ę . N a r z ę d z i e to d o d a j e k a r t ę A n g u l a r J S d o n a r z ę d z i d la p r o g r a m i s t ó w , j a k p o k a z a n o n a r y s u n k u 15.1.
Rysunek 15.1. Rozszerzenie Batarang w narzędziach dla programistów Chrome R o z s z e r z e n i e B a t a r a n g d o d a j e d o n a r z ę d z i d la p r o g r a m i s t ó w C h r o m e t r z y b a r d z o c ie k a w e i n i e z m i e r n i e p r z y d a t n e k a rt y :
Models K a rta
M o d els ( m o d e l e ) z a w i e r a p o d g l ą d n a ż y w o h i e r a r c h i i z a k r e s ó w a p l i k a c j i A n g u l a r J S .
Z n a j d z ie s z n a n ie j w s z y s t k ie z a k r e s y a k t u a ln ie u ż y w a n e w w id o k u , a z a p o m o c ą m y s z y m o ż e s z p r z e g lą d a ć i c h z m i e n n e i fu n k c je . O b o k k a ż d e g o z a k r e s u z n a jd u je się i k o n a
< — m o ż n a ją k lik n ą ć ,
a b y p r z e j ś ć d o e le m e n t u H T M L , k t ó r e g o d o t y c z y d a n y z a k r e s , i d o w ie d z ie ć się, w k t ó r y m z a k r e s ie t e n e le m e n t się z n a jd u je . P o d c z a s p r z e g lą d a n i a s t r o n y z a k r e s y s ą a k t u a liz o w a n e n a b ie ż ą c o .
Performance N a k a r c ie
P e r fo r m a n c e ( w y d a j n o ś ć ) z n a j d u j e s ię p o d g l ą d n a ż y w o w y d a j n o ś c i a p l i k a c j i
A n g u l a r J S . Jest t o p o s o r t o w a n a lis t a c z u j e k w y z w a l a n y c h w c y k l u o b l i c z e n i o w y m z w y k a z e m c z a s u w m i l i s e k u n d a c h i w u j ę c iu p r o c e n t o w y m i c h d z i a ł a n i a w c a ł y m c y k lu . I n f o r m a c j e te s ą p r z y d a t n e p r z y o p t y m a liz a c j i p r o g r a m u i s z u k a n iu w ą s k ic h g a rd e ł. W
r a z ie p o t r z e b y d a n e
m o ż n a w y e k s p o r t o w a ć d o f o r m a t u J S O N , a b y p ó ź n ie j p o r ó w n a ć je z n o w y m i d a n y m i w y g e n e r o w a n y m i p o w p r o w a d z e n i u p o p r a w e k o p t y m a liz a c y j n y c h .
Dependencies N a k a r c ie
D ep en d en cies ( z a l e ż n o ś c i ) z n a j d u j e s ię w iz u a l i z a c j a s t r u k t u r y z a l e ż n o ś c i a p lik a c j i.
K a r t a t a z a w i e r a lis t ę w s z y s t k i c h u s ł u g d o s t a r c z a n y c h i t w o r z o n y c h o r a z t y c h , o d k t ó r y c h a p lik a c j a z a le ż y . D o d a t k o w o w y ś w i e t l a n a j e s t w i z u a l n a i n f o r m a c j a o z a l e ż n o ś c i a c h m i ę d z y u s ł u g a m i itd . Jest t o ś w i e t n a p o m o c d l a k o g o ś , k t o c h c e z b a d a ć s p o s ó b d z i a ł a n i a a p lik a c j i a lb o w y k r y ć n a j w a ż n ie j s z e i n a j c z ę ś c ie j u ż y w a n e u s łu g i . Z a p o m o c ą r o z s z e r z e n i a B a t a r a n g m o ż n a p r z e a n a l i z o w a ć d o w o l n ą d z ia ła j ą c ą a p lik a c j ę A n g u l a r J S . W y s t a r c z y t y lk o w łą c z y ć n a r z ę d z ia d la p r o g r a m is t ó w i u r u c h o m ić B a t a r a n g d la d a n e j w it r y n y in t e r n e t o w e j .
272
|
Rozdział 15. Porady i najlepsze praktyki
WebStorm Zintegrowane środowisko programistyczne (IDE) lub jego brak może m ieć ogromny wpływ na szyb kość powstawania aplikacji. A w przypadku języka JavaScript, który jest pozbawiony kompilatora, zabezpieczeń typów i m a dynamiczną naturę, solidne środowisko pracy jest podstawą. Aktualnie jednym z najlepszych IDE dla języka JavaScript jest W ebStorm (http://w w w .jetbrains.com /w ebstorm /) firmy Jetbrains. A co najważniejsze, środowisko to jest doskonale zintegrowane z systemami AngularJS i Karma. Pracę programiście niezwykle ułatwiają następujące funkcje: • automatyczne uzupełnianie atrybutów H TM L AngularJS, ja k n g -c lic k , n g -cla s s itd.; • możliwość przechodzenia do zewnętrznej (internetowej) dokumentacji każdej dyrektywy pod czas jej używania; • kliknięcie z w ciśniętym przyciskiem Ctrl (lub C o m m a n d ) dyrektywy, funkcji lub kontrolera w kodzie H TM L, powodujące przejście do definicji tej konstrukcji; • dodatki pom ocne przy refaktoryzacji, np. narzędzia do zm ieniania nazw zm iennych i w ła sności, a nawet dyrektyw; • szablony do tw orzenia szkieletów typowych konstrukcji, ja k kontrolery i dyrektywy; • integracja z systemem Karma, pozwalająca wykonywać testy jednostkowe bezpośrednio w śro dowisku W ebStorm . To zaledwie niewielka część wszystkich opcji. Tworzenie aplikacji AngularJS w środowisku W eb Storm je st wysoce zalecane, warto więc pobrać 30-dniow ą wersję próbną tego narzędzia, aby je przynajm niej przetestować. Na pewno się nie zawiedziesz.
Moduły opcjonalne Na koniec przedstawiamy kilka modułów opcjonalnych udostępnianych przez rdzeń AngularJS, które również od czasu do czasu m ogą być przydatne: ngCookies Od lat ciasteczka to dane w form ie tekstowej. M oduł ngCookies zapewnia usługę umożliwia jącą pracę z ciasteczkami przeglądarki poprzez obiekt. Zamiast pracować z łańcucham i, można bezpośrednio zapisywać klucze tego obiektu i pobierać je jako obiekty. Moduł ten jest niezwykle przydatny w każdej aplikacji posługującej się ciasteczkami. n g San itize Jeżeli aplikacja wykorzystuje treść w ygenerow aną przez użytkowników i wyświetla ją w in terfejsie użytkow nika, to m oże być podatna na ataki typu X S S , zw łaszcza gdy użytkow nik wpisze kod JavaScript i H TM L w m iejsce, w którym powinien wpisać zwykły tekst. M oduł n g San itize dostarcza A PI i dyrektywy do określania, które dane wejściowe m ają być spraw dzane i ja k należy je wyrenderować. Istnieje m ożliwość wyboru renderowania treści dokład nie w takiej postaci, w jakiej została wpisana, w form acie H TM L, a nawet w form acie H TM L z m ożliwością wykonania kodu JavaScript i CSS. M oduł ten jest niezbędny każdemu, kto pra cuje z treścią przekazywaną przez użytkowników.
Narzędzia i biblioteki
| 273
ngResource M oduł ngResource opisaliśmy w rozdziale 6., w podrozdziale „M oduł ngResource”. Jeśli uży wasz serwera udostępniającego końców ki R E ST , to m oduł ngResource m oże przyczynić się do znacznego zredukowania ilości kodu, jaką będziesz m usiał napisać. ngMessages Moduł ngMessages został wprowadzony w AngularJS 1.3 i opisaliśmy go w rozdziale 4., w punkcie „M oduł ngM essages”, w odniesieniu do m echanizm u obsługi błędów. M oduł ten udostępnia ulepszoną składnię i interfejs A PI do pracy z kom unikatam i o błędach w aplikacji. ngAria Podobnie jak ngMessages, moduł ngAria został wprowadzony w AngularJS 1.3. Jego celem jest podniesienie poziom u dostępności aplikacji przez zastosowanie atrybutów A RIA (http://w w w . w 3.org/T R /w ai-aria/), w których dostarcza się inform acji dla czytników ekranu i innych tech nologii wspomagających niepełnosprawnych użytkowników. Zazwyczaj wystarczające jest włą czenie modułu ngAria i wstrzyknięcie go do aplikacji, aby można było używać atrybutów ARIA w elem entach H TM L wykorzystujących dyrektywy ngModel, ngShow, ngC lick itd. W ięcej in form acji m ożna znaleźć w oficjalnej dokum entacji systemu AngularJS (https://docs.an gu larjs. org/gu ide/accessibility ). ngTouch System AngularJS charakteryzuje się niewielkimi wymaganiami pamięciowymi i m ałą liczbą zależności, dzięki czemu doskonale sprawdza się także w urządzeniach mobilnych. Ten świetny dodatek wprowadza takie dyrektywy ja k n g -sw ip e left i ng-sw iperight do pracy z ekranam i dotykowymi. Ponadto obsługuje też tzw. f a s t c l ic k , czyli technologię um ożliwiającą natych miastowe reagowanie na zdarzenia dotknięcia przez urządzenia m obilne. ngAnimate W A ngularJS 1.2 wprowadzono op cjonalny m oduł ngAnimate. U m ożliw ia on definiow anie anim acji przejść w systemie AngularJS. Przy jego użyciu m ożna animować ukrywanie i poka zywanie elem entów obsługiw anych przez dyrektywy ng-show i ng-h ide, a nawet całe widoki i dodawanie oraz usuwanie klas. Gdy m oduł ngAnimate je st dodany do aplikacji, większość dyrektyw A ngularJS dostarcza punktów zaczepienia w postaci klas CSS służące do anim acji elem entów . Chcesz, aby interfejs użytkownika wjeżdżał na stronę spoza praw ej krawędzi ekranu? Chcesz, by elem ent w pętli zastępował inne elementy i pojawiał się powoli? W szystko to, i wiele więcej, możesz zrobić przy użyciu modułu ngAnimate.
Podsumowanie W tym rozdziale opisaliśmy wysokopoziomowe m etody testow ania i najlepsze sposoby optymal nego wykorzystania testów jednostkow ych i integracyjnych. Później udzieliliśm y odpowiedzi na jedno z najczęściej zadawanych pytań dotyczących systemu AngularJS: ja k powinna wyglądać struktura folderów projektu? Przedyskutowaliśmy kilka wartych zapamiętania koncepcji oraz p o kazaliśmy, ja k się one odnoszą do operacji CRU D .
274
|
Rozdział 15. Porady i najlepsze praktyki
P ó ź n ie j p r z e s z l i ś m y d o o m ó w i e n i a k w e s t i i z w i ą z a n y c h z j u ż g o t o w ą a p lik a c j ą , c z y li z jej b u d o w a n i e m i w d r a ż a n i e m . O p i s a l i ś m y n a r z ę d z i e G r u n t i s p o s o b y j e g o in t e g r a c j i z p r o j e k t e m , a t a k ż e w s k a z a liś m y , n a c o n a le ż y z w r a c a ć u w a g ę p r z y w d r a ż a n iu p r o je k t ó w A n g u la r J S . K o le j n y m t e m a t e m b y ł y n a j le p s z e p r a k t y k i p r o g r a m o w a n i a p r z y u ż y c i u A n g u l a r J S , z a r ó w n o d o t y c z ą c e z a g a d n i e ń o g ó l n y c h , j a k i k o n k r e t n y c h , c z y l i k o n t r o l e r ó w , d y r e k t y w , u s ł u g i f ilt r ó w . N a z a k o ń c z e n i e d o k o n a liś m y p r z e g lą d u n a j c ie k a w s z y c h n a r z ę d z i i b ib lio t e k , t a k ic h j a k B a t a r a n g i W e b S t o r m , o ra z o p c jo n a ln y c h m o d u łó w d o s t a rc z a n y c h w ra z z s y st e m e m A n g u la rJ S . T o j u ż k o n i e c n a s z e j w s p ó l n e j p r z y g o d y . S t a r a l i ś m y s ię w m i a r ę s z c z e g ó ł o w o o p i s a ć w s z y s t k i e s k ł a d n i k i s y s t e m u , t a k a b y d a ło s ię c o ś z t e g o z r o z u m ie ć . O m ó w i e n i e w s z y s t k i e g o w k s ią ż c e o t a k ie j o b j ę t o ś c i je st a b s o lu t n i e n i e m o ż l iw e , ale z d o b y t a w ie d z a s t a n o w i s o l i d n ą p o d s t a w ę , n a k t ó re j m o ż n a s z y b k o z b u d o w a ć in t e r e s u j ą c e i w y d a j n e a p lik a c j e s ie c io w e . U c z s ię n o w y c h r z e c z y i p r z y łą c z s ię d o n a s z e j s p o ł e c z n o ś c i!
Podsumowanie
| 275
276
|
Rozdział 15. Porady i najlepsze praktyki
Skorowidz
A adres szablonu, 186 URL, 92, 124 AngularJS, 15 cykl życia, 211 dyrektywy, 179 filozofia systemu, 17 filtry, 135 formularze, 61 funkcje pomocnicze, 237 testowanie jednostkowe, 47 usługi, 61, 85 zalety systemu, 17 API usługi $http, 108 aplikacja powitalna, 25 aplikacje Ajax, 174 jednostronicowe, 153 typu CRUD, 262 atrybut replace, 199 when-select, 197
B biblioteka, 271 AngularJS, 164 jQuery, 10, 164 biblioteki zewnętrzne, 264 blok beforeEach, 57 budowanie projektu, 266
C CDN, content delivery network, 267 ciągła integracja, 260 controllerAs, 32 CRUD, create, read, update, delete, 18 cykl digest cycle, 75 obliczeniowy, 214 życia, 211, 213 życia dyrektywy, 215 czujki, 245
D debugowanie, 253 tras, 157 DOM, Document Object Model, 18 dostawca, 96 $httpProvider, 111 $routeProvider, 156 dyrektywa, 19, 27, 179, 270 form-element, 233, 235 ng-app, 164, 253 ng-bind, 35, 169 ng-checked, 81 ng-form, 77, 78 ng-include, 180, 182, 201 ng-messages, 71, 72 ng-model, 61, 63, 228 ng-model-options, 75 ng-repeat, 35, 39, 41, 42 ng-submit, 65 ng-switch, 183, 201 ng-transclude, 219, 220 ng-view, 162, 164 noUiSlider, 229
277
dyrektywa open-source, 37 pieChart, 240 stock-widget, 221 tabs, 225 ui-router, 176 widżetu giełdowego, 204 dyrektywy cykl życia, 215 klasowe, 189 kontrolery, 223 nazwa, 185 opcje podstawowe, 185 opcje zaawansowane, 211 procedura testowania, 203 sposób użycia, 188 testowanie, 203 wejściowe, 228 działanie filtra, 136, 140, 141 kontrolera, 31
E element , 164