background image
background image

Tytuł oryginału: AngularJS

Tłumaczenie: Robert Górczyński

ISBN: 978-83-246-9990-2

© 2014 Helion S.A.

Authorized Polish translation of the English edition AngularJS, ISBN 9781449344856 
© 2013 Brad Green and Shyam Seshadri.

This translation is published and sold by permission of O’Reilly Media, Inc., which owns or 
controls all rights to publish and sell the same.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by 
any means, electronic or mechanical, including photocopying, recording or by any 
information storage retrieval system, without permission from the Publisher.

Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu 
niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą 
kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, 
magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji.

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź 
towarowymi ich właścicieli.

Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce 
informacje były kompletne i rzetelne. Nie bierze jednak żadnej odpowiedzialności ani
za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych
lub autorskich. Wydawnictwo HELION nie ponosi również żadnej odpowiedzialności 
za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce.

Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)

Pliki z przykładami omawianymi w książce można znaleźć pod adresem: 
ftp://ftp.helion.pl/przyklady/angula.zip

Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres 
http://helion.pl/user/opinie/angula
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.

Printed in Poland.

• 

Kup książkę

• 

Poleć książkę 

• 

Oceń książkę 

• 

Księgarnia internetowa

• 

Lubię to! » Nasza społeczność

background image

 

 

 

 

 

3

Spis treļci

Wprowadzenie ...........................................................................................7

Konwencje zastosowane w ksiñĔce 8
UĔycie przykäadowych kodów 

8

Podziökowania 9

Rozdziaĥ 1. Wprowadzenie do AngularJS  ................................................11

Koncepcje 12
Przykäad — koszyk na zakupy 

18

Co dalej? 

21

Rozdziaĥ 2. Anatomia aplikacji AngularJS ...............................................23

Wywoäanie AngularJS 

23

Architektura MVC 

24

Szablony i doäñczanie danych 

27

Organizacja zaleĔnoĈci za pomocñ moduäów 51
Formatowanie danych za pomocñ filtrów 

55

Zmiana widoków za pomocñ tras i usäugi $location 

57

Komunikacja z serwerem 

61

UĔycie dyrektyw do zmiany elementów drzewa DOM 

63

Weryfikacja danych wejĈciowych uĔytkownika 65
Co dalej? 

67

Rozdziaĥ 3. Programowanie w AngularJS ...............................................69

Organizacja projektu 

70

Narzödzia 73
Uruchamianie aplikacji 

75

Testowanie w AngularJS 

76

Testy jednostkowe 

79

Kup książkę

Poleć książkę

background image

_ Spis 

treļci

Testy typu E2E/integracji 

80

Kompilacja 82
Inne wspaniaäe narzödzia 84
Narzödzie Yeoman — optymalizacja sposobu pracy 

88

Integracja AngularJS i RequireJS 

92

Rozdziaĥ 4. Analiza aplikacji AngularJS  .................................................101

Aplikacja 101
Relacje miödzy modelem, kontrolerem i szablonem 

102

Kontrolery, dyrektywy i usäugi 105
Testy 122

Rozdziaĥ 5. Komunikacja z serwerami  .................................................. 129

Komunikacja za pomocñ usäugi $http 

129

Testy jednostkowe 

135

Praca z zasobami RESTful 

137

Usäuga $q i obietnica 

143

Przechwycenie odpowiedzi 

145

Kwestie bezpieczeþstwa 146
XSRF 147

Rozdziaĥ 6. Dyrektywy  ........................................................................... 149

Dyrektywy i weryfikacja kodu HTML 

149

Ogólny opis API 

150

Co dalej? 

170

Rozdziaĥ 7. Inne kwestie  .........................................................................171

Usäuga $location 

171

Metody moduäu AngularJS 

178

Komunikacja miödzy zasiögami

za pomocñ $on, $emit i $broadcast 

182

Ciasteczka 184
Internacjonalizacja i lokalizacja 

185

Oczyszczanie kodu HTML i moduä Sanitize 

188

Kup książkę

Poleć książkę

background image

  

 

Spis 

treļci 

_ 

5

Rozdziaĥ 8. Ļciéga i podpowiedzi ...........................................................191

Opakowanie kontrolki jQuery datepicker 

191

Lista klubów sportowych — filtrowanie i komunikacja 

196

Przekazywanie plików w aplikacji AngularJS 

201

UĔycie biblioteki Socket.IO 

204

Prosta usäuga stronicowania 

207

Praca z serwerami i logowaniem 

210

Podsumowanie 214

Skorowidz ............................................................................................... 216

Kup książkę

Poleć książkę

background image

_ Spis 

treļci

Kup książkę

Poleć książkę

background image

101

ROZDZIAĤ 4.

Analiza aplikacji AngularJS

W rozdziale 2. przedstawiono pewne najczöĈciej uĔywane funkcje framewor-

ka AngularJS, natomiast w rozdziale 3. zajöliĈmy siö zagadnieniami zwiñ-
zanymi ze sposobem prowadzenia prac programistycznych. Zamiast konty-
nuowaè  wñtek i podobnie szczegóäowo omawiaè poszczególne funkcje,
w tym rozdziale przejdziemy do maäej, rzeczywistej aplikacji. Na jej pod-
stawie dowiesz siö, jak poäñczyè ze sobñ omówione dotñd fragmenty caäoĈci
i utworzyè rzeczywistñ, dziaäajñcñ aplikacjö.

Zamiast od razu przedstawiaè caäñ aplikacjö, bödziemy jñ poznawaè w ma-

äych czöĈciach, omawiaè interesujñce zagadnienia zwiñzane z danym frag-
mentem i tym samym powoli budowaè kompletnñ aplikacjö, która bödzie
gotowa, zanim ukoþczysz lekturö rozdziaäu.

Aplikacja

GutHub to prosta aplikacja przeznaczona do zarzñdzania przepisami kuli-
narnymi. Zostaäa zaprojektowana w taki sposób, aby przechowywaè przepi-
sy kulinarne i jednoczeĈnie pokazywaè róĔne interesujñce aspekty aplikacji
AngularJS. Oto cechy charakteryzujñce tworzonñ przez nas aplikacjö:

ma ukäad skäadajñcy siö z dwóch kolumn;

pasek nawigacyjny znajduje siö po lewej stronie;

pozwala na dodawanie nowych przepisów kulinarnych;

umoĔliwia przeglñdanie istniejñcych przepisów kulinarnych.

Widok gäówny aplikacji znajduje siö po prawej stronie. W zaleĔnoĈci od
adresu URL ulega ona zmianie i wyĈwietla listö przepisów kulinarnych,

Kup książkę

Poleć książkę

background image

102 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

szczegóäy dotyczñce konkretnego przepisu lub edytowalny formularz po-
zwalajñcy na dodanie nowego bñdĒ na edycjö istniejñcego. Uruchomionñ
aplikacjö pokazano na rysunku 4.1.

Rysunek 4.1. GutHub, czyli prosta aplikacja przeznaczona do zarzñdzania przepisami
kulinarnymi

Caäa aplikacja jest dostöpna w repozytorium GitHub na stronie: https://github.
com/shyamseshadri/angularjs-book/tree/master/chapter4

.

Relacje miýdzy modelem,
kontrolerem i szablonem

Zanim przejdziemy do omawiania aplikacji, zatrzymajmy siö na chwilö
i zastanówmy, jak trzy fragmenty aplikacji wspóädziaäajñ ze sobñ oraz jak
powinniĈmy je traktowaè.

Model

 jest istotñ aplikacji. Powtórz to zdanie kilkakrotnie. Dziaäanie caäej

aplikacji opiera siö na modelu, od którego zaleĔñ: wyĈwietlany widok, dane
wyĈwietlane przez widok, zapisywane informacje i dosäownie wszystko.
Warto wiöc poĈwiöciè nieco czasu i dokäadnie przemyĈleè model — jakie
wäaĈciwoĈci powinien mieè obiekt modelu, jak bödzie pobierany z serwera,
jak bödzie zapisywany i tak dalej. Ze wzglödu na to, Ĕe uaktualnienie wido-
ku nastöpuje automatycznie dziöki uĔyciu wiñzania danych, uwagö naleĔy
skoncentrowaè na modelu.

Kup książkę

Poleć książkę

background image

Relacje miýdzy modelem, kontrolerem i szablonem

103

Kontroler

 zawiera logikö biznesowñ i okreĈla miödzy innymi: jak bödzie po-

bierany model, jakie bödñ rodzaje operacji przeprowadzanych na modelu, ja-
kich informacji widok potrzebuje z modelu, a takĔe jak przeksztaäciè model,
aby uzyskaè potrzebne dane. Ponadto przeprowadzanie weryfikacji, wy-

konywanie wywoäaþ do serwera, umieszczanie odpowiednich danych w wi-
doku oraz wäaĈciwie wszystko inne powiñzane z wymienionymi dziaäania-
mi to równieĔ aktywnoĈè definiowana w kontrolerze.

I na koniec szablon okreĈla sposób prezentacji modelu oraz interakcji uĔyt-
kownika z aplikacjñ. Zadania wykonywane przez szablon powinny byè ogra-
niczone do wymienionych poniĔej:

wyĈwietlanie modelu;

definiowanie sposobów, na jakie uĔytkownik moĔe korzystaè z aplikacji
— klikniöcia, pola danych wejĈciowych i tak dalej;

nadawanie stylu aplikacji oraz okreĈlanie, jak i kiedy pewne elementy
majñ byè wyĈwietlane — pokazywanie lub ukrywanie i tak dalej;

filtrowanie i formatowanie danych (zarówno wejĈciowych, jak i wyj-

Ĉciowych).

Trzeba pamiötaè,  Ĕe szablon w AngularJS niekoniecznie jest widokiem
w architekturze MVC (model – widok – kontroler). Zamiast tego widok jest
skompilowanñ wersjñ wykonywanego szablonu, rodzajem poäñczenia sza-
blonu i modelu.

W szablonie nie naleĔy umieszczaè  Ĕadnego rodzaju logiki biznesowej
ani definiowaè zachowania — tego rodzaju dane powinny znajdowaè siö

w kontrolerze. Zachowanie prostoty szablonów pozwala na wäaĈciwñ sepa-
racjö obowiñzków, a ponadto na przetestowanie wiökszoĈci kodu za po-
mocñ jedynie testów jednostkowych. Szablony powinny byè testowane za
pomocñ testów scenariuszy.

W tym miejscu mógäbyĈ zapytaè: gdzie naleĔy umieszczaè polecenia od-
powiedzialne za modyfikacje obiektowego modelu dokumentu? Operacje
na elementach drzewa DOM nie powinny byè definiowane w kontrolerach

lub szablonach. Najlepszym miejscem dla nich sñ dyrektywy AngularJS,
choè czasami wspomniane operacje mogñ byè stosowane za pomocñ usäug,
co pozwala na unikniöcie powielania kodu. Przykäad takiego rozwiñzania
w aplikacji GutHub równieĔ zostanie zaprezentowany i omówiony.

Bez zbödnych ceregieli przechodzimy wiöc do modelu.

Kup książkę

Poleć książkę

background image

104 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Model

W omawianej aplikacji staramy siö zachowaè maksymalnñ prostotö modelu

— bödñ to po prostu przepisy kulinarne. Wspomniane przepisy to jedyny

obiekt modelu w caäej aplikacji. Wszystkie pozostaäe komponenty zostanñ

zbudowane wokóä modelu.
KaĔdy przepis skäada siö z nastöpujñcych wäaĈciwoĈci:

identyfikator, jeĈli przepis zostaä zapisany na serwerze,

nazwa,

krótki opis,

sposób przygotowania,

informacje o ewentualnym wyróĔnieniu przepisu,

tablica skäadników podanych w postaci nazwy, iloĈci i jednostki miary.

I to tyle, model jest niezwykle prosty. Pozostaäe komponenty aplikacji utwo-

rzymy na podstawie wymienionego modelu. PoniĔej przedstawiono przy-

käadowy przepis kulinarny w formacie JSON (przepis ten zostaä pokazany

na rysunku 4.1 we wczeĈniejszej czöĈci rozdziaäu):

{
  "id": "1",

  "title": "Ciasteczka",

  "description": "Wyborne, chrupiÈce na zewnÈtrz, ciÈgliwe" +
  " w Ărodku i ociekajÈce pysznÈ czekoladÈ " +

  "ciasteczka. Najlepsze w swoim rodzaju.",

  "ingredients": [

  {
    "amount": "1",

    "amountUnits": "opakowanie",

    "ingredientName": "Chips Ahoy"

  }
  ],

  "instructions": "1. Kup opakowanie Chips Ahoy\n" +

    "2. Podgrzej ciasteczka w piekarniku\n" +

    "3. Rozsmakuj siÚ w ciepïych ciasteczkach\n" +
    "4. Z innego ěródïa naucz siÚ, jak wypiekaÊ wyborne ciasteczka"

}

W celu zachowania prostoty przykäadu nie bödziemy zajmowaè siö serwe-

rem, z którego przepisy kulinarne sñ pobierane lub w którym sñ zapisy-

wane. Kod serwera znajduje siö w repozytorium w serwisie GitHub, a do

jego uruchomienia säuĔy polecenie 

node web-server.js

, które trzeba wydaè

z poziomu podstawowego katalogu aplikacji GutHub. Przechodzimy teraz

do znacznie bardziej skomplikowanych funkcji interfejsu uĔytkownika, jakie

moĔna utworzyè na postawie naszego prostego modelu.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

105

Kontrolery, dyrektywy i usĥugi

Wreszcie moĔemy zajñè siö ciekawszymi aspektami tworzonej przez nas

aplikacji. W pierwszej kolejnoĈci spojrzymy na kod dyrektyw i usäug i po-
wiemy sobie nieco o sposobie jego dziaäania. Nastöpnie przejdziemy do
wielu kontrolerów niezbödnych do zapewnienia prawidäowego dziaäania
tworzonej aplikacji.

Usĥugi

PoniĔej przedstawiono kod Ēródäowy usäug.

// Plik: app/scripts/services/services.js.
var services = angular.module('guthub.services', ['ngResource']);

services.factory('Recipe', ['$resource', function($resource) {
  return $resource('/recipes/:id', {id: '@id'});
}]);

services.factory('MultiRecipeLoader', ['Recipe', '$q', function(Recipe, $q) {
  return function() {
    var delay = $q.defer();
    Recipe.query(function(recipes) {
      delay.resolve(recipes);
    }, function() {
      delay.reject('Nie moĝna pobraÊ przepisów kulinarnych.');
    });
    return delay.promise;
  };
}]);

services.factory('RecipeLoader', ['Recipe', '$route', '$q', function(Recipe,
$route, $q) {
  return function() {
    var delay = $q.defer();
    Recipe.get({id: $route.current.params.recipeId}, function(recipe) {
      delay.resolve(recipe);
    }, function() {
      delay.reject('Nie moĝna pobraÊ przepisu '  + $route.current.params.recipeId);
    });
    return delay.promise;
  };
}]);

Najpierw zajmiemy siö usäugami. Nie jest to nasze pierwsze spotkanie
z usäugami — zetknöliĈmy siö z nimi juĔ w rozdziale 2. Tutaj zostanñ omó-
wione nieco dokäadniej.

W przedstawionym pliku znajdujñ siö trzy usäugi AngularJS.

Kup książkę

Poleć książkę

background image

106 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Istnieje usäuga przepisu kulinarnego, która zwraca tak zwany AngularJS
Resource. Jest to zasób RESTful prowadzñcy do serwera RESTful. Wspo-
mniany zasób hermetyzuje dziaäajñcñ na niskim poziomie usäugö 

$http

, a tym

samym programista musi utworzyè jedynie kod odpowiedzialny za pracö

z obiektami.

Za pomocñ tylko pojedynczego wiersza kodu (

return $resource

) oraz oczywi-

Ĉcie zaleĔnoĈci w module 

guthub.services

 obiekt 

Recipe

 moĔe byè uĔyty jako

argument w dowolnym kontrolerze — zostanie wówczas wstrzykniöty do
wskazanego kontrolera. Co wiöcej, kaĔdy obiekt 

Recipe

 ma wbudowane

wymienione poniĔej metody:

Recipe.get()

,

Recipe.save()

,

Recipe.query()

,

Recipe.remove()

,

Recipe.delete()

.

JeĔeli zamierzasz uĔyè metody Recipe.delete() i chcesz zapewniè
dziaäanie aplikacji w przeglñdarce Internet Explorer, wtedy mu-

sisz uĔyè wywoäania w postaci Recipe['delete'](). Wynika to

z faktu, Ĕe delete jest säowem kluczowym w przeglñdarce In-
ternet Explorer.

Z wymienionych powyĔej metod wszystkie poza 

query()

 dziaäajñ z poje-

dynczym przepisem kulinarnym. Natomiast wartoĈciñ zwrotnñ metody

query()

 jest domyĈlnie tablica przepisów kulinarnych.

Wiersz kodu deklarujñcy zasób (

return $resource

) wykonuje równieĔ kilka

innych uĔytecznych zadaþ.

 

1. 

Zwróè uwagö na 

:id

 w adresie URL wskazanym dla zasobu RESTful.

Wspomniany identyfikator oznacza, Ĕe w trakcie wykonywania dowol-
nego zapytania (na przykäad za pomocñ 

Recipe.get()

), jeĈli przekaĔesz

obiekt wraz z wäaĈciwoĈciñ 

id

, wówczas jej wartoĈè zostanie umiesz-

czona na koþcu adresu URL.
Oznacza to, Ĕe wywoäanie 

Recipe.get({id: 15})

 faktycznie bödzie wy-

woäaniem do /recipe/15.

 

2. 

MógäbyĈ zapytaè w tym miejscu: co z drugim obiektem, na przykäad 

{id:

@id}

? Wiersz kodu jest wart tysiñca säów objaĈnienia, wiöc przejdĒmy

od razu do odpowiedniego przykäadu.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

107

Przyjmujemy zaäoĔenie,  Ĕe dostöpny jest obiekt 

Recipe

 zawierajñcy

wszystkie niezbödne informacje, miödzy innymi wartoĈè 

id

.

Wspomniany obiekt moĔna zapisaè za pomocñ poniĔszego fragmentu

kodu:

// PrzyjĊto zaáoĪenie, Īe obiekt existingRecipeObj ma wszystkie niezbĊdne wáaĞciwoĞci,
// w tym id (na przykáad o wartoĞci 13).
var recipe = new Recipe(existingRecipeObj);
recipe.$save();

Przedstawiony kod spowoduje wykonanie Ĕñdania 

POST

 do /recipe/13.

Fragment 

@id

 powoduje pobranie wartoĈci wäaĈciwoĈci 

id

 obiektu i uĔy-

cie jej jako parametru 

id

. Takie rozwiñzanie przyjöto dla wygody — po-

zwala ono zaoszczödziè kilka wierszy kodu.

W pliku apps/scripts/services/services.js istniejñ jeszcze dwie inne usäugi.

Obie zaliczajñ siö do komponentów wczytujñcych: pierwsza (

RecipeLoader

)

wczytuje pojedynczy przepis, natomiast druga (

MultiRecipeLoader

) jest prze-

znaczona do wczytywania wszystkich przepisów kulinarnych. Wymienio-

ne usäugi sñ uĔywane podczas konfiguracji tras, a sposób dziaäania tych

usäug jest bardzo podobny i zostaä przedstawiony poniĔej.

 

1. 

Utworzenie obiektu wstrzymanego 

$q

 (jest to rodzaj obietnicy frameworka

AngularJS stosowanej w celu äñczenia funkcji asynchronicznych).

 

2. 

Wykonanie wywoäania do serwera.

 

3. 

OkreĈlenie obiektu wstrzymanego, gdy serwer zwraca wartoĈè.

 

4. 

Zwrot obietnicy, która bödzie uĔywana przez mechanizm routingu fra-

meworka AngularJS.

Obietnice frameworka AngularJS

Obietnica to interfejs przeznaczony do pracy z obiektami, które sñ zwra-

cane lub bödñ wypeänione w przyszäoĈci (w zasadzie sñ to akcje asyn-

chroniczne). Ogólnie rzecz biorñc, na obietnicö skäada siö obiekt oraz

funkcja 

then()

.

Aby zobaczyè zalety obietnic, spójrzmy na przykäad, w którym konieczne

jest pobranie profilu uĔytkownika:

var currentProfile = null;

var username = 'dowolnaNazwa';

fetchServerConfig(function(serverConfig) {

  fetchUserProfiles(serverConfig.USER_PROFILES, username,

    function(profiles) {
    currentProfile = profiles.currentProfile;

  });

});

Kup książkę

Poleć książkę

background image

108 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Z powyĔszym podejĈciem zwiñzanych jest kilka problemów.

 

1. 

Kod wynikowy bödzie koszmarnie powcinany, zwäaszcza jeĈli zajdzie

koniecznoĈè poäñczenia kilku wywoäaþ.

 

2. 

Bäödy  zgäaszane miödzy  wywoäaniami zwrotnymi i funkcjami majñ

tendencjö do znikania, jeĔeli nie zostanñ röcznie obsäuĔone na kaĔdym

etapie.

 

3. 

W wewnötrznym wywoäaniu zwrotnym konieczna jest hermetyza-

cja logiki zwiñzanej z dziaäaniami  przeprowadzanymi za  pomocñ

zmiennej  currentProfile bezpoĈrednio lub za pomocñ oddzielnej

funkcji.

Obietnica rozwiñzuje wymienione problemy. Zanim siö przekonasz, w jaki

sposób, najpierw spójrz na ten sam kod, ale zaimplementowany z uĔyciem

obietnic:

var currentProfile = fetchServerConfig().then(function(serverConfig) {
  return fetchUserProfiles(serverConfig.USER_PROFILES, username);

}).then(function(profiles) {

  return profiles.currentProfile;

}, function(error) {

  // Obsáuga báĊdów powstaáych w fetchServerConfig()

  // lub w fetchUserProfiles().

});

Zwróè uwagö na zalety nowego rozwiñzania.

 

1. 

Istnieje moĔliwoĈè  äñczenia wywoäaþ funkcji i nie spowoduje to

koszmaru zwiñzanego ze stosowaniem wciöè w kodzie.

 

2. 

Masz gwarancjö, Ĕe wywoäanie poprzedniej funkcji zostanie zakoþ-

czone, zanim nastñpi wywoäanie kolejnej funkcji w äaþcuchu.

 

3. 

KaĔde wywoäanie  then() pobiera dwa argumenty (oba to funkcje).

Pierwszy to funkcja wywoäywana w przypadku sukcesu operacji,

natomiast drugi to procedura obsäugi bäödów.

 

4. 

W przypadku wystñpienia bäödów w äaþcuchu wspomniany bäñd bö-

dzie propagowany przez pozostaäe procedury obsäugi bäödów. Dla-

tego teĔ bäñd w dowolnym wywoäaniu zwrotnym moĔna obsäuĔyè

na koþcu.

MógäbyĈ zapytaè: co z wywoäaniami 

resolve()

 i 

reject()

? Wywoäanie

deferred()

 to we frameworku AngularJS sposób tworzenia obietnic. Z kolei

wywoäanie 

resolve()

 powoduje speänienie obietnicy (i wywoäanie pro-

cedury obsäugi w przypadku sukcesu operacji), podczas gdy wywoäanie

reject

 powoduje wywoäanie procedury obsäugi bäödów w obietnicy.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

109

Do tego zagadnienia powrócimy jeszcze podczas konfiguracji tras.

Dyrektywy

Przechodzimy teraz do dwóch dyrektyw, które bödñ uĔywane w tworzo-

nej tutaj aplikacji.

butterbar

Ta dyrektywa bödzie pokazana lub ukryta w trakcie wczytywania in-
formacji przez stronö, a takĔe po zmianie trasy. Jest poäñczona z me-
chanizmem zmiany trasy i automatycznie zostaje ukryta lub umiesz-
czona w znaczniku na podstawie stanu strony.

focus

Ta dyrektywa jest uĔywana w celu zagwarantowania, Ĕe pewne pola
tekstowe (lub elementy) formularza sieciowego sñ aktywne.

Spójrz na przykäadowy fragment kodu:

// Plik: app/scripts/directives/directives.js.
var directives = angular.module('guthub.directives', []);

directives.directive('butterbar', ['$rootScope', function($rootScope) {
  return {
    link: function(scope, element, attrs) {
      element.addClass('hide');

      $rootScope.$on('$routeChangeStart', function() {
        element.removeClass('hide');
      });

      $rootScope.$on('$routeChangeSuccess', function() {
        element.addClass('hide');
      });
    }
  };
}]);

directives.directive('focus', function() {
  return {
    link: function(scope, element, attrs) {
      element[0].focus();
    }
  };
});

Przedstawiona dyrektywa zwraca obiekt wraz z pojedynczñ wäaĈciwoĈciñ

link

. Dokäadne omówienie tematu tworzenia wäasnych dyrektyw znajdziesz

w rozdziale 6., teraz musisz jedynie wiedzieè o dwóch rzeczach.

Kup książkę

Poleć książkę

background image

110 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

 

1. 

Dyrektywy przechodzñ przez proces skäadajñcy siö z dwóch etapów.

Na pierwszym etapie (faza kompilacji) nastöpuje wyszukanie wszystkich
dyrektyw doäñczonych do elementu drzewa DOM, a nastöpnie ich
przetworzenie. Wszelkie operacje na elementach drzewa DOM sñ

przeprowadzane na etapie kompilacji. Na koþcu fazy otrzymujesz funk-
cjö äñczñcñ.

 

2. 

Na drugim etapie (faza äñczenia — tö fazö wczeĈniej wykorzystaliĈmy)

wygenerowany szablon elementów drzewa DOM jest doäñczany do za-

siögu (

scope

). Ponadto dodawane sñ wszelkie komponenty monitorujñce

lub nasäuchujñce, co oznacza powstanie funkcjonujñcego wiñzania miö-
dzy zasiögiem 

scope

 i elementem. Wszystko, co jest powiñzane z zasiögiem

scope

, zachodzi na etapie äñczenia.

Co siö dzieje w naszej dyrektywie? Zajrzyjmy do niej i przekonajmy siö.

Dyrektywa 

butterbar

 moĔe byè uĔywana w nastöpujñcy sposób:

<div butterbar>Komunikat informujÈcy o wczytywaniu...</div>

Dziaäanie dyrektywy polega na ukryciu elementu oraz dodaniu dwóch
komponentów monitorujñcych zasiög gäówny (

scope

). Za kaĔdym razem,

gdy rozpoczyna siö zmiana trasy, nastöpuje pokazanie elementu (przez
zmianö jego klasy), a po zakoþczonej powodzeniem zmianie trasy mamy
ponowne ukrycie dyrektywy 

butterbar

.

Interesujñcñ cechñ, na którñ warto tutaj zwróciè uwagö, jest sposób wstrzyk-
niöcia 

$rootScope

 do dyrektywy. Wszystkie dyrektywy majñ bezpoĈrednie

powiñzanie z systemem wstrzykiwania zaleĔnoĈci w AngularJS, co pozwala
na wstrzykiwanie do nich usäug oraz innych niezbödnych komponentów.

Ostatnia kwestia warta uwagi to API przeznaczone do pracy z elementem.
ProgramiĈci przyzwyczajeni do biblioteki jQuery bödñ szczöĈliwi, wiedzñc,
Ĕe zastosowanie ma doskonale znana im skäadnia (

addClass

removeClass

).

Framework AngularJS implementuje pewien podzbiór wywoäaþ jQuery,
a wiöc biblioteka jQuery stanowi opcjonalnñ zaleĔnoĈè dla kaĔdego projektu
AngularJS. JeĔeli w projekcie chcesz wykorzystaè peäniö moĔliwoĈci ofe-
rowanych przez jQuery, wtedy powinieneĈ wiedzieè, Ĕe AngularJS uĔywa

jej zamiast wbudowanej implementacji.

Druga dyrektywa (

focus

) jest znacznie prostsza. Jej dziaäanie polega na wywo-

äaniu metody 

focus()

 dla bieĔñcego elementu. MoĔna jñ wywoäaè przez doda-

nie atrybutu 

focus

 do dowolnego elementu danych wejĈciowych, na przykäad:

<input type="text" focus></input>

Podczas wczytywania strony element automatycznie jest aktywny.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

_

111

Kontrolery

Po zaprezentowaniu dyrektyw i usäug moĔesz wreszcie przejĈè do kon-
trolerów, których w naszej aplikacji mamy piöè. Wszystkie zostaäy zdefi-
niowane w pojedynczym pliku (app/scripts/controllers/controllers.js), ale omó-
wimy je tutaj pojedynczo. Przechodzimy wiöc do pierwszego kontrolera

(

ListCtrl

), odpowiedzialnego za wyĈwietlenie listy wszystkich przepisów

kulinarnych przechowywanych w systemie.

app.controller('ListCtrl', ['$scope', 'recipes', function($scope, recipes) {
  $scope.recipes = recipes;
}]);

Zwróè uwagö na jednñ bardzo waĔnñ kwestiö w przypadku omawianego
kontrolera: w konstruktorze nie zawiera on Ĕadnego kodu dotyczñcego
nawiñzania poäñczenia z serwerem i pobrania przepisów kulinarnych.
Zamiast tego kod zajmuje siö obsäugñ wczeĈniej pobranych przepisów. Byè

moĔe zastanawiasz siö, jak to zostaäo zrobione. CóĔ, dokäadnñ odpowiedĒ
poznasz w sekcji poĈwiöconej routingowi, ale juĔ teraz moĔemy powie-
dzieè, Ĕe wiñĔe siö to z usäugñ 

MultiRecipeLoader

. Po prostu o tym pamiötaj.

Po zapoznaniu siö z kontrolerem 

ListCtrl

 zobaczysz, Ĕe pozostaäe sñ caä-

kiem podobne do omówionego. Mimo wszystko zaprezentujemy je po
kolei, wskazujñc przy tym interesujñce aspekty:

app.controller('ViewCtrl', ['$scope', '$location', 'recipe',
    function($scope, $location, recipe) {
  $scope.recipe = recipe;

  $scope.edit = function() {
    $location.path('/edit/' + recipe.id);
  };
}]);

Interesujñcym aspektem kontrolera 

ViewCtrl

 jest funkcja edycji udostöp-

niana obiektowi 

scope

. Zamiast pokazywaè i ukrywaè pola lub stosowaè

podobne rozwiñzanie, kontroler wykorzystuje framework AngularJS i zleca
mu wykonanie najtrudniejszych zadaþ (powinieneĈ stosowaè takie samo
podejĈcie!). Funkcja 

edit()

 po prostu zmienia adres URL na odpowiednik

przepisu kulinarnego, a AngularJS zajmuje siö resztñ. Ponadto framework
wykrywa zmianö adresu URL i wczytuje odpowiedni widok (w trybie
edycji bödzie to po prostu dany przepis kulinarny). Wspaniale!

Przechodzimy teraz do kontrolera 

EditCtrl

:

app.controller('EditCtrl', ['$scope', '$location', 'recipe',
    function($scope, $location, recipe) {
  $scope.recipe = recipe;

Kup książkę

Poleć książkę

background image

112

_

Rozdziaĥ 4. Analiza aplikacji AngularJS

  $scope.save = function() {
    $scope.recipe.$save(function(recipe) {
      $location.path('/view/' + recipe.id);
    });
  };

  $scope.remove = function() {
    delete $scope.recipe;
    $location.path('/');
  };
}]);

W tym kontrolerze nowoĈciñ  sñ metody 

save()

 i 

remove()

, które 

EditCtrl

udostöpnia obiektowi 

scope

.

Metoda 

save()

 obiektu 

scope

 dziaäa zgodnie z oczekiwaniami. Zapisuje bie-

Ĕñcy przepis kulinarny, a po zakoþczeniu operacji zapisu przekierowuje
uĔytkownika do widoku wyĈwietlajñcego ten sam przepis. Funkcja wywoäa-

nia zwrotnego jest uĔyteczna, poniewaĔ pozwala na przeprowadzenie pew-
nych operacji po zapisie.

Istniejñ dwa sposoby zapisania przepisu. Jeden z nich zostaä przedstawio-
ny w kodzie i polega na wywoäaniu funkcji 

$scope.recipe.$save()

. Takie

rozwiñzanie jest moĔliwe tylko dlatego, Ĕe 

recipe

 jest obiektem zasobu zwró-

conego przez 

RecipeLoader

.

Natomiast drugi sposób zapisu to wywoäanie:

Recipe.save(recipe);

Metoda 

remove()

 równieĔ naleĔy do prostych, a jej dziaäanie polega na

usuniöciu przepisu z obiektu 

scope

 oraz przekierowaniu uĔytkownika na

stronö gäównñ. Zwróè uwagö, Ĕe nie powoduje to rzeczywistego usuniöcia
przepisu kulinarnego z serwera. Wykonanie dodatkowego wywoäania nie
powinno byè zbyt trudne.

Kolejny kontroler nosi nazwö 

NewCtrl

:

app.controller('NewCtrl', ['$scope', '$location', 'Recipe',
    function($scope, $location, Recipe) {
  $scope.recipe = new Recipe({
    ingredients: [ {} ]
  });

  $scope.save = function() {
    $scope.recipe.$save(function(recipe) {
      $location.path('/view/' + recipe.id);
    });
  };
}]);

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

_

113

Ten kontroler jest niemal dokäadnie taki sam jak 

EditCtrl

 (jako èwiczenie

mógäbyĈ oba wymienione kontrolery poäñczyè w jeden). Jedyna róĔnica

polega na tym, Ĕe pierwszym krokiem w dziaäaniu kontrolera 

NewCtrl

 jest

utworzenie nowego przepisu kulinarnego (wspomniany przepis to zasób,

a wiöc kontroler ma funkcjö 

save()

). Caäa pozostaäa funkcjonalnoĈè nie

ulega zmianie.
Ostatni kontroler to 

IngredientsCtrl

. Jest to kontroler specjalny, ale zanim

przejdziemy do jego omówienia, spójrz na tworzñcy go kod:

app.controller('IngredientsCtrl', ['$scope', function($scope) {
  $scope.addIngredient = function() {

    var ingredients = $scope.recipe.ingredients;

    ingredients[ingredients.length] = {};

  };

  $scope.removeIngredient = function(index) {
    $scope.recipe.ingredients.splice(index, 1);

  };

}]);

Wszystkie przedstawione dotñd kontrolery sñ poäñczone z okreĈlonymi wi-

dokami w interfejsie uĔytkownika. Pod tym wzglödem kontroler 

Ingredient-

sCtrl

 dziaäa nieco inaczej. To po prostu kontroler potomny uĔywany do

edycji stron i hermetyzacji pewnych funkcji niepotrzebnych na ogólnym

poziomie. Warto w tym miejscu wspomnieè o pewnej interesujñcej kwestii.

Skoro to kontroler potomny, dziedziczy obiekt 

scope

 po kontrolerze nad-

rzödnym (w omawianym przykäadzie jest to kontroler 

EditCtrl

 lub 

NewCtrl

).

Dlatego teĔ uzyskanie dostöpu do obiektu 

$scope.recipe

 odbywa siö z po-

ziomu kontrolera nadrzödnego.
Sam kod kontrolera nie zawiera nic szczególnie interesujñcego lub unikalnego.

Dodaje kilka nowych skäadników do tablicy skäadników przepisu kulinarnego

lub teĔ usuwa okreĈlony skäadnik z listy.
W ten sposób omówiliĈmy wszystkie kontrolery. Jedyny fragment kodu

JavaScript, jaki pozostaä do przeanalizowania, dotyczy konfiguracji routingu:

// Plik: app/scripts/controllers/controllers.js.
var app = angular.module('guthub',

  ['guthub.directives', 'guthub.services']);

app.config(['$routeProvider', function($routeProvider) {
  $routeProvider.

    when('/', {

      controller: 'ListCtrl',

      resolve: {
        recipes: function(MultiRecipeLoader) {

          return MultiRecipeLoader();
        }
      },

Kup książkę

Poleć książkę

background image

114 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

      templateUrl:'/views/list.html'
    }).when('/edit/:recipeId', {
      controller: 'EditCtrl',
      resolve: {
        recipe: function(RecipeLoader) {
          return RecipeLoader();
        }
      },
      templateUrl:'/views/recipeForm.html'
    }).when('/view/:recipeId', {
      controller: 'ViewCtrl',
      resolve: {
        recipe: function(RecipeLoader) {
          return RecipeLoader();
        }
      },
      templateUrl:'/views/viewRecipe.html'
    }).when('/new', {
      controller: 'NewCtrl',
      templateUrl:'/views/recipeForm.html'
    }).otherwise({redirectTo:'/'});
}]);

Zgodnie z wczeĈniejszñ obietnicñ docieramy do miejsca, w którym uĔy-

wana jest funkcja 

resolve()

. W poprzednim fragmencie kodu skonfigu-

rowano moduä 

guthub

 AngularJS, a takĔe trasy i szablony wykorzystywane

w aplikacji.
Kod  äñczy dyrektywy z utworzonymi przez nas usäugami, a nastöpnie

wskazuje róĔne trasy, które bödñ stosowane w aplikacji.
Dla kaĔdej trasy definiowany jest adres URL, kontroler odpowiedzialny za ob-

säugö danego adresu, wczytywany szablon, a takĔe (wreszcie) obiekt 

resolve

.

Obiekt 

resolve

 nakazuje frameworkowi AngularJS speänienie wymagaþ

kaĔdego klucza, zanim trasa bödzie mogäa zostaè  uĔyta do wyĈwietlenia
odpowiedniego widoku uĔytkownikowi. Zadanie aplikacji polega na wczy-
taniu wszystkich przepisów kulinarnych (lub tylko wskazanego), a serwer
ma udzieliè odpowiedzi przed wyĈwietleniem strony uĔytkownikowi. Do-
stawcö tras informujemy wiöc o posiadaniu przepisów kulinarnych (lub
przepisu), a nastöpnie podajemy mu sposób, w jaki majñ byè pobrane dane.
W trakcie wykonywania operacji pobierania danych wykorzystywane sñ

dwie usäugi (

MultiRecipeLoader

 i 

RecipeLoader

) zdefiniowane na poczñtku

tworzenia aplikacji. Framework AngularJS zostaä doĈè sprytnie zaprojek-

towany — jeĔeli wartoĈciñ zwrotnñ funkcji 

resolve()

 bödzie obietnica An-

gularJS, wtedy framework poczeka na speänienie wspomnianej obietnicy
przed przejĈciem dalej. Oznacza to koniecznoĈè zaczekania, aĔ serwer udzieli
odpowiedzi.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

_

115

Wynik jest w postaci argumentów (o nazwach parametrów bödñcych po-
lami obiektu) przekazywany konstruktorowi.

Na koþcu funkcja 

otherwise()

 wskazuje domyĈlny adres URL dla przekie-

rowania, jeĈli nie nastñpi dopasowanie Ĕadnej trasy.

Byè moĔe zauwaĔyäeĈ, Ĕe kontrolery EditCtrl i NewCtrl korzy-
stajñ z tego samego szablonu, czyli views/recipeForm.html. Co siö tutaj
dzieje? Po prostu ponownie wykorzystaliĈmy szablon przezna-

czony do edycji przepisu kulinarnego. Szablon wyĈwietla róĔne

elementy w zaleĔnoĈci od wywoäanego kontrolera.

Po zakoþczeniu omawiania kontrolerów moĔemy przejĈè do szablonów.
Zobaczysz, w jaki sposób wymienione kontrolery zostaäy powiñzane z sza-
blonami, a takĔe dowiesz siö, jak zarzñdzaè danymi, które sñ wyĈwietlane

uĔytkownikowi.

Szablony

Rozpoczynamy od zapoznania siö z najbardziej zewnötrznym, gäównym
szablonem zdefiniowanym w pliku index.html. Stanowi on podstawö dla
naszej aplikacji skäadajñcej siö z pojedynczej strony, a wszystkie pozostaäe
widoki sñ wczytywane w kontekĈcie omawianego tutaj szablonu:

<!DOCTYPE html>
<html lang="pl" ng-app="guthub">
<head>
  <title>GutHub - tworzenie przepisów kulinarnych i dzielenie siÚ nimi</title>
  <script src="scripts/vendor/angular.min.js"></script>
  <script src="scripts/vendor/angular-resource.min.js"></script>
  <script src="scripts/directives/directives.js"></script>
  <script src="scripts/services/services.js"></script>
  <script src="scripts/controllers/controllers.js"></script>
  <link href="styles/bootstrap.css" rel="stylesheet">
  <link href="styles/guthub.css" rel="stylesheet">
</head>
<body>
  <header>
    <h1>GutHub</h1>
  </header>

  <div butterbar>Wczytywanie...</div>

  <div class="container-fluid">
    <div class="row-fluid">
      <div class="span2">
        <!-- Pasek boczny. -->

        <div id="focus"><a href="#/new">Nowy przepis</a></div>

Kup książkę

Poleć książkę

background image

116 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

        <div><a href="#/">Lista przepisów</a></div>
      </div>
      <div class="span10">
        <div ng-view></div>
      </div>
    </div>
  </div>
</body>
</html>

W przedstawionym szablonie istnieje piöè elementów, na które warto zwró-
ciè uwagö. WiökszoĈè z nich miaäeĈ okazjö poznaè w rozdziale 2. Wspo-
mniane elementy omówimy po kolei.

ng-app

Ustawienie moduäu dla aplikacji GutHub. Jest to dokäadnie ten sam
moduä, który wykorzystaliĈmy we funkcji 

angular.module()

. W ten sposób

framework AngularJS wie, jak wszystko ma zostaè ze sobñ poäñczone.

script znacznik

W tym miejscu nastöpuje wczytanie AngularJS w aplikacji. Framework
trzeba wczytaè przed wszystkimi plikami JavaScript, które go uĔywa-

jñ. W idealnej sytuacji znaczniki odpowiedzialne za wczytywanie
skryptów JavaScript powinny znajdowaè siö na koþcu pliku szablonu.

butterbar

Aha! To pierwsze uĔycie naszej wäasnej dyrektywy. Ta dyrektywa zo-
staäa zdefiniowana wczeĈniej i chcemy jñ wykorzystaè wraz z elemen-
tem wyĈwietlanym podczas zmiany trasy. Po zakoþczeniu powodze-
niem operacji zmiany trasy element powiñzany z dyrektywñ 

butterbar

powinien zostaè ukryty. Dyrektywa powoduje wyĈwietlenie tekstu
(w omawianym przypadku jest to nudny komunikat 

Wczytywanie...

), gdy

zachodzi potrzeba.

’Ècza href wartoĂci

To äñcza 

href

 do róĔnych stron naszej aplikacji skäadajñcej siö z poje-

dynczej strony. Zwróè uwagö na uĔycie znaku 

#

 gwarantujñcego, Ĕe

strona nie zostanie ponownie wczytana. Adresy sñ podawane wzglö-
dem strony bieĔñcej. Framework AngularJS monitoruje wspomniane
adresy URL (dopóki strona nie zostanie ponownie wczytana) i wykonuje
caäñ pracö zwiñzanñ z ich obsäugñ (w rzeczywistoĈci jest to bardzo

nudne zarzñdzanie trasami zdefiniowane przez nas wczeĈniej wraz
z trasami), gdy zachodzi potrzeba.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

_

117

ng-view

W tym miejscu wykonywana jest pozostaäa czöĈè pracy. WczeĈniej we
fragmencie rozdziaäu poĈwiöconym kontrolerom zdefiniowaliĈmy trasy.
CzöĈciñ definicji jest adres URL trasy, powiñzany z niñ kontroler i sza-
blon. Kiedy framework AngularJS wykryje zmianö trasy, wtedy na-
stöpuje wczytanie szablonu, doäñczenie do niego kontrolera oraz za-

stñpienie elementu 

ng-view

 zawartoĈciñ szablonu.

Jedynñ rzeczñ rzucajñcñ siö w oczy jest brak znacznika 

ng-controller

. Wiök-

szoĈè aplikacji zawiera pewnego rodzaju kontroler 

MainController

 powiñzany

z szablonem gäównym. Najczöstszym miejscem jego podania jest znacznik

<body>

. W omawianej aplikacji nie uĔywamy wspomnianego znacznika,

poniewaĔ caäy szablon gäówny nie zawiera treĈci AngularJS wymagajñcej

odwoäania do obiektu 

scope

.

Spójrzmy teraz na szablony powiñzane z poszczególnymi kontrolerami. Na

poczñtek przyglñdamy siö szablonowi wyĈwietlajñcemu listö przepisów

kulinarnych:

<!-- Plik: chapter4/guthub/app/views/list.html. -->
<h3>Lista przepisów</h3>

<ul class="recipes">
  <li ng-repeat="recipe in recipes">

    <div><a ng-href="#/view/{{recipe.id}}">{{recipe.title}}</a></div>

  </li>

</ul>

To naprawdö bardzo nudny szablon. Znajdujñ siö tutaj jedynie dwa intere-

sujñce punkty. Pierwszy to standardowy sposób uĔycia znacznika 

ng-repeat

.

Zadanie wymienionego znacznika polega na pobraniu wszystkich przepisów

z obiektu 

scope

, a nastöpnie ich wyĈwietleniu.

Drugi interesujñcy punkt to uĔycie znacznika 

ng-href

 zamiast 

href

. Ma to

na celu unikniöcie wygenerowania nieprawidäowego äñcza podczas wczyty-

wania frameworka AngularJS. Znacznik 

ng-href

 gwarantuje, Ĕe w Ĕadnej

chwili uĔytkownikowi nie zostanie wyĈwietlony nieprawidäowy znacznik.

Wymienionego znacznika powinieneĈ uĔywaè zawsze, gdy adresy URL sñ

dynamiczne, a nie statyczne.
Byè moĔe zadajesz sobie pytanie: gdzie podziaä siö kontroler? Nie mamy

zdefiniowanego znacznika 

ng-controller

 i tak naprawdö nie ma zdefinio-

wanego kontrolera gäównego. W tym miejscu do gry wchodzi mapowanie

tras. MoĔe pamiötasz (mówiliĈmy o tym kilka stron wczeĈniej), Ĕe trasa /

powoduje przekierowanie do wyĈwietlajñcego listö przepisów kulinarnych

szablonu, któremu przypisano kontroler 

ListCtrl

. Dlatego teĔ wszelkie od-

niesienia do zmiennych pozostajñ w zasiögu wymienionego kontrolera.

Kup książkę

Poleć książkę

background image

118 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Teraz przechodzimy do znacznie ciekawszego szablonu, czyli odpowiedzial-
nego za wyĈwietlenie przepisu.

<!-- Plik: chapter4/guthub/app/views/viewRecipe.html. -->
<h2>{{recipe.title}}</h2>

<div>{{recipe.description}}</div>

<h3>Skïadniki</h3>

<span ng-show="recipe.ingredients.length == 0">Brak skïadników</span>
<ul class="unstyled" ng-hide="recipe.ingredients.length == 0">
  <li ng-repeat="ingredient in recipe.ingredients">
    <span>{{ingredient.amount}}</span>
    <span>{{ingredient.amountUnits}}</span>
    <span>{{ingredient.ingredientName}}</span>
  </li>
</ul>

<h3>Sposób przygotowania</h3>
<div>{{recipe.instructions}}</div>

<form ng-submit="edit()" class="form-horizontal">
  <div class="form-actions">
    <button class="btn btn-primary">Edycja</button>
  </div>
</form>

To kolejny maäy, przydatny szablon. Warto zwróciè uwagö na dwa punkty
powyĔszego szablonu, choè niekoniecznie w kolejnoĈci ich wymienienia.

Pierwszy to caäkiem standardowy sposób uĔycia dyrektywy 

ng-repeat

.

Przepisy kulinarne znajdujñ siö w zasiögu kontrolera 

ViewCtrl

 wczytanego

przez funkcjö 

resolve()

 przed wyĈwietleniem strony uĔytkownikowi. Dziöki

temu gwarantujemy prawidäowe dziaäanie strony, gdy zostaje wyĈwietlona.

Drugi punkt to uĔycie dyrektywy 

ng-submit

 w formularzu. Wymieniona

dyrektywa oznacza, Ĕe wysäanie formularza spowoduje wywoäanie funkcji

edit()

 obiektu 

scope

. Wysäanie formularza nastöpuje, gdy klikniöty bödzie

przycisk niepowiñzany z Ĕadnñ funkcjñ (w omawianym przypadku to
przycisk  Edycja). I znów dziaäanie frameworka AngularJS zostaäo zaprojek-
towane bardzo sprytnie — potrafi on prawidäowo ustaliè zasiög, do którego
ma siö odwoäywaè (na przykäad: moduäu, trasy lub kontrolera), i wywoäaè
odpowiedniñ metodö we wäaĈciwym czasie.

Teraz moĔemy przejĈè do ostatniego (i prawdopodobnie najbardziej skom-
plikowanego) szablonu, czyli formularza pozwalajñcego na dodanie lub edy-

cjö przepisu kulinarnego.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

_

119

<!-- Plik: chapter4/guthub/app/views/recipeForm.html. -->
<h2>Edycja przepisu</h2>
<form name="recipeForm" ng-submit="save()" class="form-horizontal">
  <div class="control-group">
    <label class="control-label" for="title">Nazwa:</label>
    <div class="controls">
      <input ng-model="recipe.title" class="input-xlarge" id="title" focus required>
    </div>
  </div>

  <div class="control-group">
    <label class="control-label" for="description">Opis:</label>
    <div class="controls">
      <textarea ng-model="recipe.description" class="input-xlarge"
        id="description"></textarea>
    </div>
  </div>

  <div class="control-group">
    <label class="control-label" for="ingredients">Skïadniki:</label>
    <div class="controls">
      <ul id="ingredients" class="unstyled" ng-controller="IngredientsCtrl">
        <li ng-repeat="ingredient in recipe.ingredients">
          <input ng-model="ingredient.amount" class="input-mini">
          <input ng-model="ingredient.amountUnits" class="input-small">
          <input ng-model="ingredient.ingredientName">
          <button type="button" class="btn btn-mini"
            ng-click="removeIngredient($index)"><i class="icon-minus-sign"></i>
          Usuñ</button>
        </li>
        <button type="button" class="btn btn-mini" ng-click="addIngredient()">
        <i class="icon-plus-sign"></i>Dodaj</button>
      </ul>
    </div>
  </div>

  <div class="control-group">
    <label class="control-label" for="instructions">Sposób
przygotowania:</label>
    <div class="controls">
      <textarea ng-model="recipe.instructions" class="input-xxlarge"
      id="instructions"></textarea>
    </div>
  </div>

  <div class="form-actions">
    <button class="btn btn-primary" ng-disabled="recipeForm.$invalid">Zapisz
    </button>
    <button type="button" ng-click="remove()" ng-show="!recipe.id" class="btn">
    Usuñ</button>
  </div>
</form>

Kup książkę

Poleć książkę

background image

120 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Nie panikuj! Wyglñda na to, Ĕe szablon zawiera caäkiem sporñ iloĈè kodu,
i faktycznie tak jest. Jednak po rzeczywistym zagäöbieniu siö weþ moĔna
siö przekonaè,  Ĕe kod nie jest skomplikowany. Tak naprawdö to prosta,
powtarzajñca siö struktura, pokazujñca, jak edytowalne pola tekstowe zo-

staäy zastosowane w formularzu przeznaczonym do edycji przepisów ku-
linarnych.

W pierwszym polu tekstowym (

title

) zostaäa umieszczona dyrektywa

focus

. Dziöki temu po przejĈciu na tö stronö wskazane pole zostanie

wybrane, a uĔytkownik bödzie mógä natychmiast rozpoczñè wprowa-

dzanie danych wejĈciowych.

Dyrektywa 

ng-submit

 jest uĔyta w bardzo podobny sposób jak w po-

przednim przykäadzie, a wiöc nie bödziemy jej tutaj dokäadnie oma-
wiaè. Warto wiedzieè, Ĕe powoduje zapisanie stanu przepisu kulinarne-
go i wskazuje koniec procesu edycji. Ponadto jest powiñzana z funkcjñ

save()

 zdefiniowanñ w kontrolerze 

EditCtrl

.

Dyrektywa 

ng-model

 säuĔy do poäñczenia róĔnych pól tekstowych for-

mularza sieciowego z polami modelu.

Jednym z najbardziej interesujñcych aspektów omawianej strony jest
umieszczona w czöĈci poĈwiöconej liĈcie skäadników dyrektywa 

ng-

controller

, której naprawdö warto poĈwiöciè nieco uwagi i spróbowaè

w peäni zrozumieè sposób jej dziaäania. Zobaczmy wiöc, co siö tutaj
dzieje.
Lista skäadników jest wyĈwietlana, a zawierajñcy jñ znacznik jest po-
wiñzany z dyrektywñ 

ng-controller

. Oznacza to, Ĕe caäy znacznik 

<ul>

znajduje siö w zasiögu kontrolera 

IngredientsCtrl

. MógäbyĈ w tym

miejscu zapytaè: co z rzeczywistym kontrolerem 

EditCtrl

 powiñzanym

z szablonem? Jak siö okazuje, 

IngredientsCtrl

 jest tworzony jako kon-

troler potomny 

EditCtrl

 i tym samym dziedziczy po nim. Dlatego teĔ

dostöp do obiektu 

recipe

 nastöpuje z poziomu kontrolera 

EditCtrl

.

Ponadto kontroler 

IngredientsCtrl

 dodaje metodö 

addIngredient()

  uĔy-

wanñ przez dyrektywö 

ng-click

 i dostöpnñ jedynie w zasiögu znacz-

nika 

<ul>

. Dlaczego zdecydowaliĈmy siö na takie rozwiñzanie? To naj-

lepszy sposób na rozdzielenie obowiñzków. Po co umieszczaè metodö

addIngredient()

 w kontrolerze 

EditCtrl

, skoro 99% szablonu jej nie po-

trzebuje? Kontrolery potomne i zagnieĔdĔone doskonale sprawdzajñ siö
w tego rodzaju sytuacjach i pozwalajñ na oddzielenie logiki biznesowej
przez umieszczenie jej w äatwiejszych do zarzñdzania elementach.

Kup książkę

Poleć książkę

background image

Kontrolery, dyrektywy i usĥugi

_

121

Pozostaäe dyrektywy, które chcemy tutaj omówiè, sñ kontrolkami prze-
znaczonymi do weryfikacji formularza sieciowego. We frameworku
AngularJS moĔna bardzo äatwo okreĈliè, Ĕe dane pole formularza jest
wymagane. W tym celu wystarczy dodaè do tego pola dyrektywö 

required

(jak to zrobiono w omawianym fragmencie kodu). Rodzi siö jednak
pytanie: co dalej?
Przechodzimy do przycisku Zapisz. Zwróè uwagö na uĔycie dyrekty-
wy 

ng-disabled

, która ma wartoĈè 

recipeForm.$invalid

. Czäon pierwszy

(

recipeForm

) to nazwa formularza zawierajñcego deklaracjö dyrektywy.

Framework AngularJS dodaje do niego pewne zmienne specjalne (za-
liczamy do nich 

$valid

 i 

$invalid

) pozwalajñce na kontrolowanie ele-

mentów formularza sieciowego. AngularJS wyszukuje wszystkie wy-
magane elementy, a nastöpnie odpowiednio uaktualnia wspomniane
zmienne specjalne. JeĔeli pole säuĔñce do podania nazwy przepisu kuli-
narnego pozostanie niewypeänione, wartoĈciñ 

recipeForm.$invalid

 bödzie

true

 (a wartoĈciñ 

$valid

 bödzie 

false

) i przycisk Zapisz zostanie zablo-

kowany.

Istnieje równieĔ moĔliwoĈè okreĈlenia minimalnej i maksymalnej däugoĈci
pola tekstowego, a takĔe wzorzec wyraĔenia regularnego przeznaczonego
do przeprowadzenia weryfikacji danego pola. Co wiöcej, pewne funkcje
zaawansowane moĔna wykorzystaè do wyĈwietlania komunikatów bäödów
po wystñpieniu pewnych okreĈlonych warunków. Spójrzmy na prosty
przykäad:

<form name="myForm">
  Nazwa uĝytkownika:<input type="text"
    name="userName"
    ng-model="user.name"
    ng-minlength="3">
  <span class="error"
    ng-show="myForm.userName.$error.minlength">Zbyt krótka!</span>
</form>

Za pomocñ uĔycia dyrektywy 

ng-minlength

 w powyĔszym fragmencie ko-

du zdefiniowano, Ĕe nazwa uĔytkownika musi skäadaè siö z przynajmniej
trzech znaków. Teraz formularz zostaje wypeäniony danymi pochodzñcymi
z obiektu 

scope

 — w omawianym przykäadzie to jedynie 

userName

. Wszystkie

pola tekstowe majñ obiekt 

$error

 (zawiera informacje o rodzaju ewentual-

nego bäödu: 

required

minlength

maxlength

 lub 

pattern

) oraz wäaĈciwoĈè

$valid

 wskazujñcñ poprawnoĈè bñdĒ teĔ niepoprawnoĈè danych wejĈciowych.

Kup książkę

Poleć książkę

background image

122 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Takie rozwiñzanie pozwala na selektywne wyĈwietlanie uĔytkownikowi

komunikatu bäödu w zaleĔnoĈci od jego rodzaju, jak to pokazano w powyĔ-

szym fragmencie kodu.
Do drugiego przycisku doäñczona jest dyrektywa 

ng-click

 uĔywana pod-

czas usuwania przepisu kulinarnego. Zwróè uwagö,  Ĕe przycisk jest wy-

Ĉwietlany tylko wtedy, gdy przepis nie zostaä jeszcze zapisany. Wprawdzie

znacznie sensowniejsze wydaje siö uĔycie 

ng-hide="recipe.id"

, ale czasami

bardziej semantyczne rozwiñzanie to 

ng-show="!recipe.id"

. Oznacza to wy-

Ĉwietlenie przycisku, gdy przepis kulinarny nie zawiera identyfikatora,

zamiast ukrywania przycisku, jeĈli przepis ma zdefiniowany identyfikator.

Testy

WstrzymywaliĈmy siö z przedstawieniem testów wraz z kontrolerami, ale

musiaäeĈ siö spodziewaè, Ĕe kiedyĈ wreszcie do nich przejdziemy. W tym

podrozdziale zaprezentowane zostanñ testy, które naleĔy utworzyè dla

przygotowanego dotñd fragmentu kodu. Dowiesz siö równieĔ, jak tworzy

siö takie testy.

Testy jednostkowe

NajwaĔniejszy rodzaj testów to testy jednostkowe. Pozwalajñ one na spraw-

dzenie, czy opracowane kontrolery (dyrektywy i usäugi) majñ prawidäowñ

strukturö i konstrukcjö oraz czy dziaäajñ zgodnie z oczekiwaniami.

Zanim przejdziemy do poszczególnych testów jednostkowych, warto spoj-

rzeè na szkielet przeznaczony dla wszystkich testów jednostkowych doty-

czñcych kontrolera:

describe('Kontrolery', function() {
  var $scope, ctrl;
  // W teĞcie naleĪy wskazaü moduá.

  beforeEach(module('guthub'));
  beforeEach(function() {
    this.addMatchers({
      toEqualData: function(expected) {
        return angular.equals(this.actual, expected);
      }
    });
  });

  describe('ListCtrl', function() {....});

  // Miejsce na opisanie pozostaáych kontrolerów.
});

Kup książkę

Poleć książkę

background image

Testy

123

Przygotowany szkielet (tutaj nadal wykorzystujemy styl Jasmine do tworze-
nia testów) wykonuje kilka zadaþ.

 

1. 

Tworzy globalnie (przynajmniej dla testu) dostöpny obiekt 

scope

 i kon-

troler, a wiöc nie trzeba siö przejmowaè tworzeniem nowej zmiennej
dla kaĔdego kontrolera.

 

2. 

Inicjalizuje moduä uĔywany przez aplikacjö (w omawianym przykäadzie

jest to GutHub).

 

3. 

Dodaje specjalne dopasowanie nazywane 

equalData

. Pozwala ono na prze-

prowadzanie asercji na obiektach Ēródäa (na przykäad przepisach kuli-
narnych) zwracanych przez usäugö 

$resource

 lub na wywoäanie RESTful.

Pamiötaj o koniecznoĈci dodania specjalnego dopasowania na-

zywanego equalData za kaĔdym razem, gdy zachodzi potrzeba

stosowania asercji na zwróconych obiektach ngResource. WiñĔe

siö to z faktem, Ĕe zwrócone obiekty ngResource majñ metody do-
datkowe, których zwykäe wykonanie zakoþczy siö niepowodze-

niem, poniewaĔ oczekiwane sñ wywoäania equalData.

Majñc przygotowany szkielet, spójrzmy na gotowy test jednostkowy prze-
znaczony dla kontrolera 

ListCtrl

:

describe('ListCtrl', function() {
  var mockBackend, recipe;
  // _$httpBackend_ to nazwa taka sama jak $httpBackend. Zastosowany zapis sáuĪy do odróĪnienia

  // zmiennych wstrzykniĊtych od zmiennych lokalnych.

  beforeEach(inject(function($rootScope, $controller, _$httpBackend_, Recipe) {
    recipe = Recipe;
    mockBackend = _$httpBackend_;
    $scope = $rootScope.$new();
    ctrl = $controller('ListCtrl', {
      $scope: $scope,
      recipes: [1, 2, 3]
    });
  }));

  it('Wynikiem powinna byÊ lista przepisów kulinarnych', function() {
    expect($scope.recipes).toEqual([1, 2, 3]);
  });
});

Jak zapewne pamiötasz, kontroler 

ListCtrl

 naleĔy do najprostszych w apli-

kacji. Konstruktor kontrolera pobiera po prostu listö przepisów, a nastöp-
nie zapisuje je w obiekcie. Wprawdzie moĔna do tego utworzyè test, ale

wydaje siö to zbödne. W omawianym przykäadzie mimo wszystko utwo-
rzyliĈmy test, poniewaĔ testy jednostkowe sñ wspaniaäe!

Kup książkę

Poleć książkę

background image

124 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Znacznie ciekawiej robi siö w przypadku usäugi 

MultiRecipeLoader

. Wy-

mieniona usäuga jest odpowiedzialna za pobranie listy przepisów kulinar-
nych z serwera i przekazanie ich jako argumentu (kiedy zastosowana jest
prawidäowa konfiguracja za pomocñ usäugi 

$route

):

describe('MultiRecipeLoader', function() {
  var mockBackend, recipe, loader;
  // _$httpBackend_ to nazwa taka sama jak $httpBackend. Zastosowany zapis sáuĪy do odróĪnienia

  // zmiennych wstrzykniĊtych od zmiennych lokalnych.

  beforeEach(inject(function(_$httpBackend_, Recipe, MultiRecipeLoader) {
    recipe = Recipe;
    mockBackend = _$httpBackend_;
    loader = MultiRecipeLoader;
  }));

  it('Wynikiem powinno byÊ wczytanie listy przepisów kulinarnych', function() {
    mockBackend.expectGET('/recipes').respond([{id: 1}, {id: 2}]);

    var recipes;

    var promise = loader();
    promise.then(function(rec) {
      recipes = rec;
    });

    expect(recipes).toBeUndefined();
    mockBackend.flush();
    expect(recipes).toEqualData([{id: 1}, {id: 2}]);
  });
});
// Miejsce na opisanie pozostaáych kontrolerów.

Test usäugi 

MultiRecipeLoader

 odbywa siö przez przygotowanie usäugi 

Http

´

Backend

 w naszym teĈcie. Obiekt pochodzi z pliku angular-mocks.js i jest

doäñczany w trakcie przeprowadzania testów. Po prostu wstrzykniöcie go
do metody 

beforeEach()

 jest wystarczajñce, aby moĔna byäo konfigurowaè

oczekiwania. W drugim, znacznie ciekawszym teĈcie oczekiwanie zostaäo
zdefiniowane jako wywoäanie 

server GET

 do recipes, a wynikiem powinna

byè tablica obiektów. Nastöpnie uĔywamy dopasowania w celu spraw-
dzenia, czy uzyskany wynik jest dokäadnie zgodny z oczekiwaniami. Zwróè
uwagö na wywoäanie 

flush()

 w obiekcie makiety, przekazujñce odpowiedĒ

pochodzñcñ z serwera. Tego rodzaju mechanizm moĔna wykorzystaè do
przetestowania przepäywu kontroli i sprawdzenia, jak aplikacja dziaäa przed

otrzymaniem odpowiedzi z serwera i po jej otrzymaniu.

Pomijamy kontroler 

ViewCtrl

, poniewaĔ jest niemal identyczny z 

ListCtrl

,

poza dodatkiem w postaci metody 

edit()

. Wymieniona metoda jest bardzo

äatwa do przetestowania: wystarczy wstrzyknñè usäugö 

$location

 do testu

i sprawdziè jej wartoĈè.

Kup książkę

Poleć książkę

background image

Testy

125

Przechodzimy teraz do kontrolera 

EditCtrl

, który z perspektywy testów

jednostkowych ma dwa interesujñce punkty. Funkcja 

resolve()

 jest podobna

do uĔywanej juĔ poprzednio i moĔe byè przetestowana w dokäadnie ten
sam sposób jak wczeĈniej w rozdziale. Zamiast tego zobaczysz teraz, jak

moĔna przetestowaè metody 

save()

 i 

remove()

. Spójrzmy wiöc na wymienione

testy (przyjöto zaäoĔenie o uĔyciu szkieletu przedstawionego w poprzednim
przykäadzie):

describe('EditCtrl', function() {
  var mockBackend, location;
  beforeEach(inject(function($rootScope,
      $controller,
      _$httpBackend_,
      $location,
      Recipe) {
    mockBackend = _$httpBackend_;
    location = $location;
    $scope = $rootScope.$new();

    ctrl = $controller('EditCtrl', {
      $scope: $scope,
      $location: $location,
      recipe: new Recipe({id: 1, title: 'Przepis'})
    });
  }));

  it('Wynikiem powinien byÊ zapisany przepis kulinarny', function() {
    mockBackend.expectPOST('/recipes/1',
      {id: 1, title: 'Przepis'}).respond({id: 2});

    // Ustawienie innej wartoĞci, aby mieü gwarancjĊ jej zmiany podczas testu.

    location.path('test');

    $scope.save();
    expect(location.path()).toEqual('/test');

    mockBackend.flush();

    expect(location.path()).toEqual('/view/2');
  });

  it('Wynikiem powinno byÊ usuniÚcie przepisu kulinarnego', function() {
    expect($scope.recipe).toBeTruthy();
    location.path('test');

    $scope.remove();

    expect($scope.recipe).toBeUndefined();
    expect(location.path()).toEqual('/');
  });
});

Kup książkę

Poleć książkę

background image

126 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

W pierwszym teĈcie sprawdzane jest dziaäanie funkcji 

save()

. W szczegól-

noĈci upewniamy siö, Ĕe operacja zapisu powoduje wykonanie do serwera
Ĕñdania 

POST

 wraz z obiektem. Nastöpnie, po udzieleniu odpowiedzi przez

serwer, przechodzimy na stronö zawierajñcñ nowo zapisany przepis kulinarny.

Drugi test jest jeszcze prostszy. Po prostu sprawdzamy, czy wywoäanie
funkcji 

remove()

 powoduje usuniöcie wskazanego przepisu kulinarnego,

a nastöpnie przekierowujemy uĔytkownika na stronö docelowñ. Jest to äatwe
do wykonania dziöki wstrzykniöciu usäugi 

$location

 do testu i jej uĔyciu.

Pozostaäa czöĈè testów jednostkowych dla kontrolerów wykorzystuje te same
wzorce, a wiöc moĔna je tutaj pominñè. Ogólnie rzecz ujmujñc, testy jed-
nostkowe opierajñ siö na kilku aspektach:

zagwarantowanie,  Ĕe kontroler (lub bardziej prawdopodobnie obiekt

scope

) osiñgnie prawidäowy stan na koþcu procesu inicjalizacji;

potwierdzenie wykonania prawidäowych wywoäaþ serwera i osiñgniöcie

wäaĈciwego stanu przez obiekt 

scope

 w trakcie wspomnianych wywo-

äaþ serwera oraz po ich zakoþczeniu (do tego celu w testach jednost-
kowych uĔywany jest obiekt makiety);

wykorzystanie funkcji wstrzykiwania zaleĔnoĈci we frameworku An-

gularJS, aby uzyskaè uchwyt do elementów i obiektów uĔywanych przez
kontroler. Pozwala to upewniè siö, Ĕe kontroler ustawia prawidäowy stan.

Testy scenariuszy

Gdy testy jednostkowe zakoþczñ siö powodzeniem, moĔe pojawiè siö po-
kusa zakoþczenia pracy. Jednak praca programisty AngularJS nie koþczy
siö, zanim nie bödñ przeprowadzone testy scenariuszy. Wprawdzie testy

jednostkowe dajñ gwarancjö,  Ĕe kaĔdy najmniejszy fragment kodu Java-
Script dziaäa zgodnie z oczekiwaniami, ale jednoczeĈnie warto siö upewniè
o wczytaniu szablonów, prawidäowym powiñzaniu kontrolerów i poprawnej
reakcji na klikniöcia elementów szablonu.

Dokäadnie do tego celu säuĔñ testy scenariuszy we frameworku AngularJS.
Pozwalajñ one na:

wczytanie aplikacji;

przejĈcie na konkretnñ stronö;

klikniöcie i wprowadzenie tekstu;

upewnienie siö o prawidäowej reakcji aplikacji.

Kup książkę

Poleć książkę

background image

Testy

127

Na jakiej zasadzie dziaäa test scenariusza dla strony wyĈwietlajñcej listö
przepisów kulinarnych? Przede wszystkim przed rozpoczöciem rzeczywi-
stego testu trzeba poczyniè pewne przygotowania.

Aby ten test scenariusza dziaäaä, konieczne jest przygotowanie serwera

WWW, który bödzie miaä moĔliwoĈè akceptacji Ĕñdaþ wykonywanych
przez aplikacjö GutHub, a takĔe pobierania listy przepisów kulinarnych
z testowanej aplikacji oraz ich przechowywania. MoĔesz zmodyfikowaè kod
i wykorzystaè zapisywanñ w pamiöci listö przepisów — wymaga to usu-
niöcia 

$resource

 dla przepisu i zmiany usäugi na obiekt zawierajñcy dane

w formacie JSON. Ewentualnie moĔna ponownie wykorzystaè i zmodyfiko-
waè serwer WWW przedstawiony w poprzednim rozdziale bñdĒ teĔ uĔyè

narzödzia Yeoman!

Po przygotowaniu i uruchomieniu serwera WWW obsäugujñcego aplikacjö
wystarczy utworzyè i wykonaè przedstawiony poniĔej test:

describe('GutHub App', function() {
  it('Wynikiem powinna byÊ lista przepisów kulinarnych', function() {
    browser().navigateTo('/index.html');
    // DomyĞlna lista przepisów kulinarnych w aplikacji GutHub skáada siĊ jedynie z dwóch pozycji.

    expect(repeater('.recipes li').count()).toEqual(2);
  });
});

Kup książkę

Poleć książkę

background image

128 _

Rozdziaĥ 4. Analiza aplikacji AngularJS

Kup książkę

Poleć książkę

background image

  

 

 

 

217

Skorowidz

A

analiza aplikacji, 101
anatomia aplikacji, 23
API, 150
API HTML5, 174
API jQuery, 165
aplikacje

AJAX, 57
mobilne, 61
sieciowe, 11

architektura MVC, 14, 24
arkusze stylów CSS, 39
asynchroniczne wywoäania metod, 130
atak typu XSRF, 147
atrybut

href, 42
multiple, 203
ng-app, 19
ng-change, 30
ng-controller, 44
ng-model, 20, 45, 193
ng-repeat, 19, 37

required, 66
src, 42

B

bezpieczeþstwo, 146
biblioteka

jQuery, 110
NodeJS, 76
RequireJS, 92
Socket.IO, 76, 129, 204

blok

Config, 179
Run, 179

bäödy, 173
buforowanie odpowiedzi, 134

C

ciasteczka, 184

D

dane wejĈciowe, 65
debugowanie, 85
definiowanie kontrolerów, 111
deklaracja

dyrektywy, 201
kontrolera, 194
zasobu, 140

dodawanie trasy, 90
doäñczanie danych, 14, 20, 27
DOM, Document Object Model, 14, 63, 164
dostöp do

konsoli, 87
zasiögu, 160

dwukropek, 140
dyrektywa, 17, 149

butterbar, 109, 116, 211
errorMessage, 212
expander, 167
focus, 65, 95, 109

ng-app, 24
ng-bind, 29
ng-bind-html, 189
ng-bind-html-unsafe, 189
ngbkFocus, 65
ng-class, 40
ng-click, 65, 122
ng-controller, 120
ng-disabled, 67
ng-hide, 38
ng-minlength, 121
ng-model, 120
ngPluralize, 186
ng-repeat, 37, 41, 118, 196, 200

Kup książkę

Poleć książkę

background image

218  _ Skorowidz

dyrektywa

ng-style., 40
ng-submit, 32, 118, 120
ng-view, 58

dyrektywy

dostöp do zasiögu, 160
funkcja compile(), 157
funkcja link(), 157
nazwa, 152
obsäuga zdarzeþ, 32

opcja priority, 153
opcja templates, 154
opcja transclude, 157
wäaĈciwoĈè restrict, 152

dyskretny kod JavaScript, 33
dziaäanie

dyrektywy, 110
filtru, 57
funkcji save(), 126
opcji dyrektywy, 164
testu, 99
usäugi $location, 174

E

edycja przepisu, 118

element

<input>, 66
ng-app, 116
ng-href, 117
ng-repeat, 117
ng-show, 38
ng-view, 117

elementy

drzewa DOM, 36, 164
powtarzalne, 36
szablonu, 116
tablicy, 42

F

faza

kompilacji, 158

äñczenia, 158

filtr, 55

filterService, 200
linky, 189, 190

filtrowanie, 196

daty i godziny, 188
listy, 80

formatowanie danych, 55
formularz, 29
formularz rejestracyjny, 65
framework

BDD, 79
Express, 75

funkcja

$http.get(), 130
$resource, 140
$scope. safeApply(), 173
$watch(), 30, 45–50
addExpander(), 168
callback, 208
callMe(), 50
compile(), 158
computeNeeded(), 31
controller(), 165
directive(), 63
done, 203
edit(), 118
equals(), 143

factory(), 53, 181
focus(), 64, 110
inheritedData(), 165
injector(), 165
link(), 64, 158
otherwise(), 58, 115
provider(), 53, 181
remove(), 21, 112
resolve(), 114, 118, 125
run(), 156
save(), 112
scope(), 165
select(), 194
selectRow(), 41
service(), 53, 181

StartUpController(), 31
stun(), 39
then(), 107, 143
totalCart(), 47

funkcje

jQuery, 165
typu getter, 175
typu setter, 175
usäugi $location, 174

Kup książkę

Poleć książkę

background image

  

 

Skorowidz

_ 219

G

granice aplikacji, 24

grupowanie zaleĔnoĈci, 51

H

harmonijka, 168

hermetyzacja, 142

I

IDE, 73

identyfikator lokalizacji, 186

informacje o lokalizacji, 173

instalacja

Karma, 96

Yeoman, 89

integracja z IDE, 79

interceptor, 212

interfejs

document.cookie, 184

uĔytkownika, 19

internacjonalizacja aplikacji, 185, 188

K

karta

kredytowa, 139

Model, 86

Performance, 86

katalog

app, 93

config, 93

test, 93

klasa HelloController, 13

kod

lokalizacji, 186

serwera, 75

usäugi, 105

kompilacja, 82

kompilator Closure Compiler, 83

komponent nasäuchujñcy, 183

komunikacja

miödzy kontrolerami, 196

miödzy zasiögami, 182

z serwerami, 61, 129

z usäugami, 211

konfiguracja

moduäu, 180

routingu, 113

Ĉrodowiska programistycznego, 92

testów jednostkowych, 96

zasiögu, 161

Ĕñdania, 131

konstruktor kontrolera, 167

kontroler, 14, 25, 43, 103, 166

CartController, 21, 47

EditCtrl, 111, 120, 125

FilterCtrl, 196

HelloController, 13

IngredientsCtrl, 113

ListCtrl, 111, 117, 123, 196

NamesListCtrl, 136

NewCtrl, 112

RootController, 212

SearchController, 185

ViewCtrl, 111

kontrolka datepicker, 191, 194

koszyk na zakupy, 18, 48

L

lista, 36

logika

aplikacji, 14, 65

biznesowa, 142

lokalizacja, 185, 186

luka w zabezpieczeniach, 146

Ĥ

äñcza

bezwzglödne, 177

href, 116

wzglödne, 177

M

menedĔer NPM, 77

metoda, Patrz funkcja

metody

konfiguracji moduäu, 180

moduäu AngularJS, 178

obiektu Recipe, 106

obiektu zdarzenia, 184

Kup książkę

Poleć książkę

background image

220  _ Skorowidz

model, 14, 25, 102
moduä

gäówny, 179
guthub, 114
ngResource, 138
ngSanitize, 189

Sanitize, 188

modyfikacja

ciasteczka, 148

Ĕñdania, 132

monitorowanie

elementów, 50

zmian, 45

MVC, 14, 24

N

nagäówek

Authorization, 213

DO NOT TRACK, 133

nagäówki

HTTP, 133

uwierzytelnienia, 211

narzödzia, 73, 84

narzödzie

Ant, 92

Batarang, 85

karta Model, 86

karta Performance, 86

wäaĈciwoĈci elementów, 87

zaleĔnoĈci usäugi, 87

Karma, 76–78, 96

RequireJS, 92

Scenario Runner, 80, 82

WebStorm, 73

Yeoman, 70, 75

dodawanie tras, 90

funkcje, 88

instalacja, 89

testy, 91

tworzenie projektu, 90

nasäuchiwanie zdarzeþ, 183

nawias klamrowy, 28

nazwa dyrektywy, 152

ngResource, 142

notacja

{{ }}, 20

interpolacji, 39

NPM, Node Package Manager, 77

O

obiekt

$scope, 44
config, 131, 135
Recipe, 106
resolve, 114
scope, 136
zdarzenia, 184

obiektowy model dokumentu, 14

obiekty wstrzymane, 107
obietnica, 107, 143
obsäuga

bäödów, 145, 210
HTML5, 63
kodów stanu, 212
liczby mnogiej, 186
lokalizacji, 187
äñczy, 176
przekierowaþ, 211
RequireJS, 96
zdarzeþ, 32, 33

ochrona przed lukñ, 147
oczyszczanie kodu HTML, 188
opakowanie kontrolki jQuery, 191

opcje

dyrektywy, 151
wäaĈciwoĈci require, 167

operacje

bitowe, 42
logiczne, 42
matematyczne, 42
po stronie serwera, 142

optymalizacja, 83

Simple, 83
zaawansowana, 83

organizacja projektu, 70, 92

P

pasek

nawigacyjny, 35

tytuäu, 162

plik

angular.js, 187
app.js, 94
controller.js, 60
controllers.js, 64, 95

Kup książkę

Poleć książkę

background image

  

 

Skorowidz

_ 221

detail.html, 59
index.html, 58, 64, 95
karma.config.js, 78
list.html, 59
main.js, 95, 98

pliki

aplikacji, 70
JavaScript, 70
konfiguracyjne, 72, 78
szablonów HTML, 71

pokazywanie elementów, 38
pole

combo, 200
tekstowe, 29, 120
wyboru, 200
wyszukiwania, 199

porównania, 42
prawo Demeter, 17
produkty, 55
programowanie, 69
projekt jQuery-File-Upload, 201
prototypowe dziedziczenie, 26
przechwycenie odpowiedzi, 145
przedstawianie danych, 14

przekazywanie plików, 201
przenoszenie treĈci, 157
przepisy kulinarne, 102
przepisywanie äñczy, 177
przycisk zerowania, 32
publikacja danych modelu, 44

S

Scenario Runner, 80, 82
schematy weryfikacji HTML, 150
serwer

Karma, 78
RESTful, 106
Socket.IO, 206
WWW, 75, 90

serwis GitHub, 62

strategie wiñzania, 161
strefy czasowe, 188
stronicowanie, 207
styl Jasmine, 80, 123
style CSS, 39
szablon, 15, 17, 27, 54, 103
szablon po stronie klienta, 12

Ļ

ĈcieĔki app/img, 71
Ĉrodowisko IDE, 73, 79

T

tabela, 36
tablica currentPageItems, 209
TDD, Test-driven development, 76
technika TDD, 76, 80
test, 99

ACID, 35
integracji, 72, 80
jednostkowy, 72, 79, 122, 135, 142
kontrolera, 136
metody, 125
scenariusza, 126, 127
typu E2E, 72, 80, 99
usäugi, 124, 208

testowanie, 76
token, 148
transformacje

odpowiedzi, 134
Ĕñdania, 134

trasa, 44, 57, 114
tryb

hashbang, 175
HTML5, 175

tworzenie

aplikacji sieciowych, 11
dyrektyw, 109, 150
filtrów, 56
funkcji dyrektywy, 63
interfejsu uĔytkownika, 14
obiektu wstrzymanego, 107
obietnic, 108
paska, 162
projektu, 90, 91
szablonu, 12, 115
trasy, 57
usäug, 53

zasiögu, 160

typ zasiögu, 161

Kup książkę

Poleć książkę

background image

222  _ Skorowidz

U

uaktualnianie listy, 196
ukrywanie

bäödów, 212
elementów, 38

uruchamianie

aplikacji, 28, 75
serwera, 90
testów, 91

röczne, 81
zautomatyzowane, 81

usäuga, 53, 105

$cookies, 185
$cookieStore, 185
$http, 61, 129, 137, 142
$httpProvider, 135
$location, 17, 57, 124, 171

funkcje, 174
integracja AngularJS, 173
integracja HTML5, 174
tryb hashbang, 175
tryb HTML5, 175

$q, 143
$route, 57, 124

$routeProvider, 57
Authentication, 213, 214
Error, 211
errorService, 211
filterService, 196
MultiRecipeLoader, 124
Pagination, 210
Paginator, 208
stronicowania, 207

uwierzytelnienie Ĕñdania, 213
uĔycie

atrybutu ng-model, 45
biblioteki Socket.IO, 204
dyrektywy, 153
dyrektywy focus, 65

filtrów, 196
funkcji $watch(), 45, 48
kontrolerów, 43, 166
Pythona, 76
Scenario Runner, 82
serwera WWW, 75, 81
transformacji, 135
usäugi $location, 174

WebStorm, 74
wyraĔenia, 45
Yeoman, 75
zasobów AngularJS, 138

W

wczytywanie

moduäu, 179
skryptu, 23

weryfikacja

danych wejĈciowych, 65
kodu HTML, 149
pól formularza, 66

wiñzanie select, 193
widok, 14, 25, 44
wielokrotne

uĔycie kodu, 170
uĔycie komponentów, 170

wäaĈciwoĈci

AngularJS, 89
elementów, 87
obiektu, 42
obiektu zdarzenia, 184

wäaĈciwoĈè

$scope.isDisabled, 40

$valid, 66
innerHtml, 15
require, 167
window.location, 171, 173

wskazanie

atrybutu select, 195
lokalizacji, 57

wstrzykiwanie

usäugi, 96
zaleĔnoĈci, 16, 126

wtyczka

Batarang, 85
FileUpload, 203

wydajnoĈè aplikacji, 83
wykonywanie testów, 98

wyraĔenia, 42
wyĈwietlanie

listy, 196
tekstu, 28, 118

wywoäania

API, 180
zwrotne, 141

Kup książkę

Poleć książkę

background image

  

 

Skorowidz

_ 223

wywoäanie

$apply, 172
AngularJS, 23
factory(), 180
provider(), 181
scope.$apply, 172
select(), 194
service(), 181

wyzerowanie wartoĈci pola tekstowego, 32
wzorzec Singleton, 183

X

XHR, 130
XSRF, 147

Z

zabezpieczenia JSON, 146
zagnieĔdĔenie zasiögów, 86
zaleĔnoĈci, 51, 71, 179

RequireJS, 94
usäugi, 87

zarzñdzanie

danymi, 14
moduäami, 179
zaleĔnoĈciami, 92

zasada minimalnej wiedzy, 17

zasiög, 44, 86, 160, 182

globalny, 26
gäówny $rootScope, 182
nadrzödny, 161, 183
odizolowany, 160
potomny, 161, 183

zasoby

AngularJS, 138
RESTful, 106, 137
statyczne, 71

zasób karty kredytowej, 139
zdarzenia zasiögu, 183
zdarzenie

click, 166
loginRequired, 211
on-select, 194
select, 195

zintegrowane Ĉrodowisko

programistyczne, IDE, 73

zlokalizowany zestaw reguä, 187
zmiana

elementów drzewa DOM, 63
widoków, 57

znacznik, Patrz element
znacznik semantyczny, 34

znak

dolara, 53
dwukropka, 140

ś

Ĕñdanie, 131

CreditCard, 139
DELETE, 138
GET, 138
POST, 107, 138
XHR, 130

Ĕywe szablony, 74

Kup książkę

Poleć książkę

background image

O autorach

Brad Green

 pracowaä nad projektem AngularJS jako menedĔer inĔynierów w Google,

gdzie jest odpowiedzialny równieĔ za kwestie zwiñzane z uäatwieniami dostöpu i pomoc
technicznñ. Przed podjöciem pracy w Google zajmowaä siö tworzeniem witryn interne-
towych dla urzñdzeþ przenoĈnych w AvantGo, zakäadaä i sprzedawaä start-upy, a kilka
lat spödziä na mozolnej pracy osoby organizujñcej przyjöcia i bankiety. Wkrótce po
ukoþczeniu szkoäy podjñä swñ pierwszñ pracö w zaäoĔonej przez Steve’a Jobsa firmie
NeXT Computer, w której odpowiadaä za przygotowywanie wersji demo oprogramowa-

nia i slajdów do prezentacji Jobsa. Brad wraz z Ĕonñ i dwójkñ dzieci mieszka w Mountain
View w stanie Kalifornia.

Shyam Seshadri

 jest wäaĈcicielem i szefem Fundoo Solutions (http://www. befundoo.com/),

gdzie czas dzieli miödzy pracö nad innowacyjnymi i ekscytujñcymi nowymi produktami
na rynek indyjski a Ĉwiadczeniem usäug konsultingowych i prowadzeniem warsztatów
dotyczñcych AngularJS. Przed utworzeniem Fundoo Solutions zdobyä tytuä magistra na
prestiĔowej indyjskiej uczelni Indian School of Business w Hajdarabadzie. Po ukoþcze-
niu szkoäy pracowaä dla Google, gdzie zajmowaä siö róĔnorodnymi projektami, miödzy
innymi Google Feedback (pierwsza aplikacja oparta na frameworku AngularJS!), oraz
innymi narzödziami i projektami wewnötrznymi. Obecnie pracuje w swoim biurze w Nawi
Mumbaj w Indiach.

Kolofon

Zwierzö widniejñce na okäadce ksiñĔki AngularJS to Lactoria fornasini — morska ryba roz-

dymkoksztaätna naleĔñca do rodziny kosterowatych (Ostraciidae) i rodzaju Lactoria. ēyje na
skalistych rafach lub piaszczystym dnie, schowana i zaplñtana pomiödzy gñbkami oraz
wodorostami. Wystöpuje w zachodniej czöĈci oceanów Spokojnego i Indyjskiego. ēywi
siö gäównie robakami i innymi bezkrögowcami.

Lactoria

 osiñga 15 centymetrów däugoĈci i od 3 do 50 centymetrów szerokoĈci. Osobniki za-

liczane do rodziny kosterowatych wyróĔniajñ siö szeĈciokñtnym wzorem päytek kost-
nych tworzñcych pancerz na ich skórze. Z ciaäa, przypominajñcego trójkñtne pudeäko,
wystajñ päetwy i ogon umoĔliwiajñce im poruszanie siö. Wraz z wiekiem ksztaät ciaäa koste-
rowatych zmienia siö od nieco zaokrñglonego do kwadratowego, a kolor skóry staje siö
bardziej jaskrawy.

Lactoria fornasini

 chroni siö przed niebezpieczeþstwem, wydzielajñc przez skórö substan-

cje powierzchniowo czynne uruchamiane pod wpäywem stresu. Toksyny, zwykle wydalane
w postaci Ĉluzu, przenikajñ do Ĉrodowiska, draĔniñc inne ryby i organizmy znajdujñce siö
w niedalekim sñsiedztwie.

Rysunek na okäadce pochodzi ze zbiorów Johnson’s Natural History. Czcionka na okäadce

to Adobe ITC Garamond. Czcionka tekstu to Adobe Minion Pro, czcionka w nagäów-
kach — Adobe Myriad Condensed, kody zostaäy napisane czcionkñ Dalton Maag’s
Ubuntu Mono.

Kup książkę

Poleć książkę

background image
background image