Przejdź do treści PWA

Szkolenie: AngularJS Podstawy [Relacja Live]

Szkolenie poprowadził Tomasz Sułkowski - developer pracujący w Hexagram. Od 16 miesięcy związany z firmą konsultingową Sages.

Oficjalne logo frameworka AngularJS.

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 projekcie
  • npm update - sprawdza, czy istnieją nowsze wersje pakietów + instaluje
  • npm install nazwa-pakietu --save-dev - instaluje pakiet, dodaje go do package.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 projekcie
  • bower update - sprawdza, czy istnieją nowsze wersje pakietów + instaluje
  • bower install nazwa-pakietu --save - instaluje pakiet, dodaje go do bower.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-app
  • ng-init - służy do wykonywania kodu na początku
  • ng-model - służy do prowadzenia interakcji między użytkownikiem a modelem
  • ng-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 DOMa
  • ng-repeat - służy do iteracji po tablicy
  • ng-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 😄