Szkolenie poprowadził Tomasz Sułkowski - developer pracujący w Hexagram. Od 16 miesięcy związany z firmą konsultingową Sages.
Miejsce szkolenia to siedziba firmy Sages na ul. Nowogrodzka 62c. Więcej o firmie: sages.com.pl/.
Slajdy dostępne pod adresem: slideshare.net/sagespl/podstawy-angularjs.
09:05 - Agenda
- wprowadzenie / kontekst / narzędzia
- teoria / demo
- warsztat
- teoria / demo
- warsztat
- ...
- Q & A
Korzystamy ze Slacka - IMO komunikator teraźniejszych czasów.
Na początku trochę pytań odnośnie doświadczeń.
Cel: Budujemy aplikację typu SPA
SPA - Single Page Application
Nawigacja w aplikacji typu SPA polega na żonglowaniu hashem w URLu.
Narzędzia
Tomek polecał narzędzia:
- WebStorm (IDE)
- Sublime Text 3
- Atom 1.0
- Brackets
- ... Vim?
Ze swojego doświadczenia stwierdził, że po kilku miesiącach korzystania z Submlime Text 3, postanowił przerzucić się na WebStorm, ze względu na lepsze podpowiadanie oraz na poprawianie literówek, które każdy z nas, siłą rzeczy, popełnia.
npm - Node Packaged Modules
- system zarządzania zależnościami dla "server-side js"
- zależności opisywane w pliku
package.json npm install- instaluje pakiety, których jeszcze nie ma w projekcienpm update- sprawdza, czy istnieją nowsze wersje pakietów + instalujenpm install nazwa-pakietu --save-dev- instaluje pakiet, dodaje go dopackage.json
Aby nie trzymać zależności w projekcie. Dla nowego środowiska wystarczy zainstalować jednym poleceniem biblioteki.
Dodatkowo, możemy określi konkretną wersję którą chcemy zainstalować.
Domyślnie, trzeba by było szukać biblioteki w sieci i ściągać plik na dysk - teraz wystarczy jedno polecenie.
Bower
Instalacja poleceniem npm install -g bower
- zarządzanie zależnościami dla "client-side js"
- zależności opisywane w
bower.json bower install- instaluje pakiety, których jeszcze nie ma w projekciebower update- sprawdza, czy istnieją nowsze wersje pakietów + instalujebower install nazwa-pakietu --save- instaluje pakiet, dodaje go dobower.json- pakiety instalowane są do katalogu
/bower_components/nazwa-pakietu/...- stamtąd należy je linkować w plikach html
09:30 - Ćwiczenie 1
Stworzenie nowego projektu w WebStorm.
- ctrl+shift+a - wyszukiwarka wszystkich opcji edytora WebStorm
- ctrl+shift+o - szukanie plików w projekcie
- ctrl+e - lista ostatnio otwartych plików
Złota myśl Nauka skrótów jest bardzo pomocna. Umiejętność sprawnego poruszania się w skrótach, pomaga zdecydowanie zmniejszyć czas potrzebny na wykonywanie bieżącej pracy.
Omawianie struktury wygenerowanego seed-a
Kiedy stworzyliśmy nowy projekt typu AngularJS WebStorm stworzył prostą strukturę katalogów wraz z wieloma plikami.
W kolejnym kroku czyścimy boilerplate usuwając e2e-test, index_async.html oraz 2 pliki CSS z html5-boilerplate.
Po wydaniu magicznego polecenia npm install mamy do projektu ściągnięte wszystkie zależności.
W pliku package.json w script/postinstall uruchomimy również bower install,
więc i biblioteki client-side będą zainstalowane w katalogu app/bower_components.
Gdybyśmy nie mieli żadnego serwera serwującego statyki, w zależnościach mamy wpisany http-server.
Jest to prosty serwer napisany w Node.js.
10:00 - Instalujemy Bootstrap
Dzięki poleceniu bower install --save bootstrap instalujemy Bootstrap-a.
W zależności Bootstrap jest biblioteka jQuery.
W katalogu app/bower_components znajduje się ściągnięty pełny projekt Bootstrap oraz jQuery.
TIP: Bower zawsze cache-uje sobie biblioteki, aby przy ponownym pobraniu szybko ściągnął kolejny raz zależności.
10:04 - Korzystamy z Bootstrap-a
Dołączamy Bootstrap-a do index.html dorzucając przed plikiem app.css następującą linijkę kodu:
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
Usuwanie zbędne paczki: bower uninstall angular-mocks.
Dodatkowo usuńmy zbędne komponenty app/components.
10:11 - Prezentacja docelowej aplikacji
Tomek przedstawia aplikację, do którą chcemy dziś napisać. Portal bardzo podobny do ipla.tv lub może bardziej do filmweb.pl. Przeglądanie listy seriali i wchodzimy w szczegóły danego serialu i możemy przeczytać opis. Możemy wyszukiwać dany serial po nazwie, oraz dodać dowolny serial do listy ulubionych.
10:15 - AngularJS
Posłuchamy teraz trochę więcej na temat samej biblioteki AngularJS.
Najbardziej zewnętrzną rzeczą w aplikacji jest moduł.
Każdy moduł może posiadać specyficzną konfigurację.
Na szkoleniu będziemy korzystali z jednej specyficznej usługi - routing (ui.router).
TIP: Podejście Fat model ma do siebie, że kontrolery powinny być "cienkie", czyli nie zawierały dużo kodu, ale za to modele powinny przechowywać jak najwięcej się da 😄
Moduły, struktura aplikacji
Form Follows Function- Moduły wskazuję na zależności od zewnętrznych bibliotek
- Modułu nie separują zawieranych komponentów (brak namespaces)
- Poszczególne fragmenty aplikacji jako moduły, o hierarchii odzwierciedlonej w strukturze katalogów
TIP: Gdy stworzymy usługę o tej samej nazwie, to zostanie nadpisana poprzednia usługa w innym module o tej samej nazwie.
Routing
ngRoute do jakiegoś czasu był obecny w AngularJS. Jest dosyć prosty.
Lepszym rozwiązaniem jest ui.router.
W ui.router mamy kaskady widoków - każdy widok (kawałek HTMLa) może posiadać dostęp
do innego kawałka kodu HTML, bez przeładowania całego globalnego widoku.
ngRoute niestety przeładowuje całą stronę, dzięki czemu korzystanie z aplikacji jest bardzo podobne
do typowego przeglądania stron internetowych (mam tu na myśli efekt przeładowania się całej strony).
10:33 - Ćwiczenie 2
Podmiana ngRoute na ui.router (github.com/angular-ui/ui-router).
Instalujemy ui.router poprzez wykonywanie polecenia:
bower install angular-ui-router --save
oraz usuwamy starą paczkę
bower uninstall angular-route --save
11:04 - Ścieżki w aplikacji (ui.router)
Dodajemy katalogi: app/library, app/profile, app/search.
11:12 - Dependency Injection
AngularJS zanalizuje kontrolery, wyciąga nazwy parametrów na podstawie łańcucha znaków,
który dostajemy kiedy uruchomimy toString na funkcji.
function MainController($scope, $http) {
console.log($scope, $http);
}
MainController.toString() // => 'function MainController($scope, $http) { ...'Na podstawie wyrażenia regularnego AngularJS wyciąga nazwy parametrów, których oczekujemy.
TIP: Gdy dokonamy minifikacji kodu pojawi się problem
ze wstrzykiwaniem odpowiednich modułów, ponieważ wszystkie
nazwy zmiennych zostaną zmienione na krótkie zaczynając od zwykłych liter.
Z pomocą przychodzi ngAnnotate -
github.com/olov/ng-annotate.
Dodajemy moduł ui.router.
angular.module('myApp', ['ui.router']).
config(function ($stateProvider) {
$stateProvider
.state('search', {
url: '/search',
templateUrl: 'search/search.html'
});
});
Dodatkowo podmieniamy w index.html
<!-- Następującą linijkę w pliku index.html: -->
<div ng-view></div>
<!-- Zastępujemy na taką: -->
<div ui-view></div>Tworzymy strukturę w projekcie - plik app/search/search.html.
Ustawiamy domyślną stronę poprzez $urlRouterProvider
$urlRouterProvider.otherwise('/search');11:49 - Widok
Kawałki mniejszego HTMLa, które możemy użyć w innym (większym).
11:51 - Controller
Zwykła funkcja JavaScript.
Powiązanie z widokiem odbywa się przez $scope.
TIP: $scope jest to usługa, która jest obiektem to,
co zdefiniujemy w tym obiekcie, będzie to od razu dostępne dla widoku (dyrektywy).
11:54 - Dyrektywy
... czyli HTML na sterydach! 😄
Możemy użyć na kilka sposobów:
- element
- atrybut
- klasę CSS
- komentarz
Wbudowane dyrektywy:
ng-appng-init- służy do wykonywania kodu na początkung-model- służy do prowadzenia interakcji między użytkownikiem a modelemng-show- gdy wartość będzie truly to kontener się wyświetli (dodaje klasę ng-hide - widoczność poprzez CSS)ng-if- usuwa albo dodaje dany fragment do DOMang-repeat- służy do iteracji po tablicyng-list- podczas prezentacja tablicy jako ciąg znaków, traktuje ten ciąg jako tablicę
two-way data-binding - wiązanie w obie strony, tj: widok aktualizuje model,
który od razu jest zmieniony w kontrolerze, ale i w drugą stronę, gdy kontroler zaktualizuje model,
będzie od widoczny (najszybciej jak to możliwe) widoku.
Więcej na ten temat na stronie https://docs.angularjs.org/guide/databinding.
TIP: AngularJS promuje deklaratywny styl pisania aplikacji (przeciwieństwo do imperatywnego).
Logika interfejsu jest w HTMLu!
<input type="text" ng-model="flag">
<!-- Pierwotnie: flag=undefined (AngularJS nic nie wyświetli)-->
<!-- Po kliknięciu: flag=true -->
<!-- Po odkliknięciu: flag=false -->Input jest dyrektywą!
Więcej na temat dyrektyw w dokumentacji: https://docs.angularjs.org/guide/directive.
TIP: Podczas wykonania ng-repeat tablica
nie może posiadać duplikatów.
Jeśli jednak chcemy operować na duplikatach trzeba utworzyć specjalną konstrukcję:
<ul>
<!-- Rozwiązujemy problem duplikatów -->
<li ng-repeat="name in names track by $index">
{ { name } }
</li>
</ul>12:18 - Usługi
Do czego służą usługi?
Miejsce gdzie trzymamy logikę biznesową. Wykonują się w kilku kontrolerów, więc nie ma sensu tego powtarzać. Usługi również służą do tego, aby współdzielić dane miedzy kontrolerami.
Definicja usług na 5 sposobów:
.constant()- proste wartości, nie można nadpisywać.value()- proste wartości, można nadpisywać.service()- większa usługa, która może posiadać wiele metod i właściwości.factory()- większa usługa, która zwraca jeden obiekt.provider()- generyczna usługa, bo każda jest de facto providerem
Więcej na temat usług https://docs.angularjs.org/api/ng/service/$http.
12:35 - Ćwiczenie 3
Tworzymy SearchController w katalogu app/search/,
wykorzystujemy usługę $http do pobierania listy filmów.
13:07 - Przerwa obiadowa!
14:05 - Prezentacja swoich wyszukiwarek
Rozwiązujemy problem z obrazkami poprzez ng-src.
14:33 - Tworzymy service
Bardzo prosty service operujący na kolekcji dodanych elementów. Przykładowa implementacja:
"use strict";
angular.module('myApp')
.service('library', function () {
var library = [];
this.add = function (item) {
library.push(item);
};
this.getAll = function () {
// kopia, aby nie tracić tej hermetyzacji
return library.slice();
};
this.has = function (item) {
return library.indexOf(item) !== -1;
};
this.remove = function (item) {
var index = library.indexOf(item);
if (index !== -1) {
library.splice(index, 1);
}
};
});Jako dodatkowe zajęcie dostałem zapisywanie do localStorage. Udało mi się zrobić w 5 minut 😄
Tomasz poleca portal nodeschool.io/pl/ gdzie można podszkolić się z JavaScript.
Aby obserwować jakiś model należy wykonać:
$scope.$watch('query', function (newValue, oldValue) {
console.log(newValue, oldValue);
});
Na samym początku ładowania aplikacji $watch zostanie uruchomiony i przekazane zostaną mu wartości undefined.
Można się przed tym zabezpieczyć wystarczy sprawdzić czy nowa wartość równą się starej, bo tylko na początku obie wartości będą miały
tą samo wartość.
15:22 - Dyrektywa ng-model-options
Dzięki tej dyrektywie mamy możliwość skonfigurowania komunikacji pomiędzy widokiem a kontrolerem.
<input
type="search"
ng-model="query"
ng-model-options="{ debounce: 1000 }"
/>Dzięki takiemu zastosowaniu odciążamy trochę aplikację, dodając lag na zmianę modelu.
Tomasz poleca wtyczkę do Batarang, dzięki której mamy możliwość podglądu modelów analizując DOMa.
15:30 - Directive Definition Object
- Enkapsulują logikę związaną z zachowaniem
- Tworzą widgety - małe aplikacje, klocki z których budujemy większe fragmenty
Jako zadanie domowe warto zrobić dyrektywę dla przycisków "Dodaj" i "Usuń".
16:25 - Filtry
Umożliwiają przekształcanie danych w trakcje przejścia ze $scope do widoku bez zmieniania oryginalnej wartości.
Jest około 7 standardowych filtrów. Ta mała liczba wynika ze względu na prostotę dodawania swoich.
Od AngularJS 1.3 mamy w filtrach pure function, czyli dla takich samych parametrów mamy te same wyjście, tym samym
biblioteka zapamiętuje (proces memoizacja) wynik dla tych samych parametrów.
16:39 - Formularze
TIP: Koniecznie trzeba ustawiać nazwy dla znacznika form,
ponieważ będzie on pod tą nazwą dostępny w scope.
Stany formularza
$valid- bez błędów$invalid- z błędami$pristine- nienaruszony$dirty- zmodyfikowany
16:57 - Dobre praktyki
17:08 - Kończymy na dziś!
Podziękowanie dla Tomka za całodniowe szkolenie 😄