AngularJS #7 – HTML na sterydach – wprowadzenie do dyrektyw

AngularJS



AngularJS nie byłby bez dyrektyw tym samym frameworkiem. To właśnie dyrektywy są absolutną przewagą AngularJS. Tak jak w tytule – użycie dyrektyw, to HTML na sterydach. Dzięki nim jesteśmy w stanie nauczyć nasz HTML nowych sztuczek.

Dyrektywa

Czym jest więc wspomniana dyrektywa? W poprzednich wpisach kilka razy wspominałem o dodatkowych elementach, atrybutach, komentarzach HTML oraz klasach CSS, które możemy używać w HTML dzięki wykorzystaniu AngularJS. Te dodatkowe rzeczy, to nic innego jak dyrektywy. AngularJS w fazie kompilacji ($compile) skanuje drzewo DOM naszego dokumentu HTML, a następnie w miejsce wystąpień dyrektyw podpina funkcjonalności na poziomie samego DOM. Owe funkcjonalności mają zastosowanie na poziomie elementu, dla którego zdefiniowaliśmy dyrektywę oraz wszystkich jego dzieci. Dyrektywy pozwalają w ten sposób budować reużywalne komponenty, które pozwalają manipulować drzewem DOM oraz dostarczać mu nowych funkcjonalności.

Przykłady istniejących dyrektyw znajdziecie praktycznie w każdym miejscu. ng-appng-controllerng-bind istnieją w AngularJS. W poprzednich wpisach wykorzystałem chociażby dyrektywę ng-repeat, która pozwoliła mi zbudować dynamiczną listę w oparciu o kolekcję obiektów. Dyrektywy są najczęściej wykorzystywanym elementem AngularJS i stanowią o jego sile i przewadze nad innymi frameworkami JavaScript.

Opcje

Dyrektywy tworzone mogą być na kilka różnych sposobów. Posiadają także szereg parametrów konfiguracyjnych, które odpowiadają za samo funkcjonowanie oraz użycie dyrektyw. Przejdźmy przez większość z nich.

restrict

Jak już wcześniej wspomniałem – dyrektywy możemy wywoływać w HTML na kilka sposobów. Określa to parametr restrict, który przyjmuje wartości:

restrict: 'A'
<div my-directive></div>
restrict: 'C'
<div class="my-directive: expression;"></div>
restrict: 'E'
<my-directive></my-directive>
restrict: 'M'
<!-- directive: my-directive expression -->

Możemy oczywiście podać wszystkie wartości jednocześnie: restrict: ‘EMAC’. Najbardziej popularne i chyba najrozsądniejsze jest wykorzystanie atrybutów oraz elementów HTML jako sposobu wywołań dyrektyw. Najlepiej radzą sobie z tym również przeglądarki.

template

Kolejnym elementem jest atrybut template, który pozwala nam zdefiniować szablon, który wykorzystywać będzie nasza dyrektywa. Dzięki temu możemy bindować dane przekazywane do dyrektywy lub generowane przez kontroler (patrz niżej) samej dyrektywy.

template: '<div>My extra template is here</div>'

templateUrl

Szablony umieszczać możemy również w osobnych plikach. Nierzadko będą one rozbudowane, więc tym łatwiej będzie nam je trzymać w osobnym pliku HTML, gdzie nasze IDE pozwoli nam też podpowiadać składnię, niżeli pisać szablon w postaci łańcucha znaków w JavaScript. Szablony zdefiniowane w ten sposób ładowane są asynchronicznie i cachowane.

templateUrl: 'templates/my-extra-template.html'

controller

Dyrektywy posiadać mogą własne, lokalne kontrolery, gdzie definiować możemy metody operujące na danych. Pozwala to budować logikę dyrektywy, którą następnie możemy używać chociażby w szablonie.

compile

Atrybut dyrektywy, który definiuje jej działanie w czasie kompilacji – jeszcze przed podpięciem samej dyrektywy do drzewa DOM. Faza compile zwraca funkcję link (patrz niżej), pozwalającą na bindowanie logiki dla elementu w drzewie DOM.

link

Finalne bindowanie logiki dyrektywy dla elementu w drzewie DOM. Jest wynikiem fazy kompilacji następującej przed samym linkowaniem.

require

Pozwala nam określić zależności w postaci innych dyrektyw, które wymagane są do działania naszej dyrektywy. Spójrzmy na definicję dyrektywy:

app.directive('myDirective', function () {
    return {
        restrict: 'A',
        require: '^ngModel'
    };
});

oraz przykład jej wywołania:

<div my-directive ng-model="city"></div>

Gdyby zabrakło tutaj wywołania ng-model, to otrzymalibyśmy błąd mówiący nam o tym fakcie.

Zauważyliście zapewne znak ^ przed nazwą ngModel. Informuje on kompilator AngularJS by szukać wywołania dyrektywy ngModel także poza elementem, dla którego zdefiniowano dyrektywę myDirective.

Istnieje również opcja ?, która spowoduje, że w przypadku braku zdefiniowanej zależności nie otrzymamy błędu kompilacji dyrektywy.

scope

Jak możecie się domyślać – jest to lokalny scope w naszej dyrektywie. Wywołując dyrektywę w HTML możemy do niej przekazywać różne parametry.

Spójrzmy na kawałek kodu:

scope: {
    param1: '@',  // parametr przekazywany przez wartosc (one-way binding)
    param2: '=',  // parametr przekazywany przez referencje (two-way binding)
    param3: '&'   // wyrazenie, ktore moze zostac wywolane w ciele dyrektywy
}

Pozwoli nam to zdefiniować dyrektywę w następujący sposób:

<div my-directive param1="{{ onlyValue }}" param2="toWayBindingByReference" param3="functionInvokedInDirective()"></div>

replace

replace: true

Pozwala na zamianę ciała elementu, dla którego definiujemy w całości przez szablon dyrektywy.

transclude

Możemy w ten sposób pobrać zawartość elementu, dla którego definiujemy dyrektywę, a następnie przekazać ją do szablonu dyrektywy.

transclude: true

Wyobraźmy sobie HTML w postaci:

<div my-directive>
    <p>To template please!</p>
</div>

Wykorzystanie opcji transclude w dyrektywie pozwoli nam na pobranie <p>To template please!</p> oraz przekazanie do szablonu w następujący sposób:

app.directive('myDirective', function () {
    return {
        restrict: 'A',
        template: '<div ng-transclude></div>',
        transclude: true
    };
});

Tym samym paragraf p przekazany zostanie do div szablonu dyrektywy.

Pierwsza dyrektywa

Poniżej przykład budowy oraz wykorzystania dyrektywy, która pozwalać będzie na wstawienie w dane miejsce dokumentu HTML reużywalnego komponentu w postaci listy z tytułem.

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/Ggur3/2/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Egghead

“First Directive”

[youtube_sc url=”https://www.youtube.com/watch?v=xoIHkM4KpHM”]

“Directive Restrictions”

[youtube_sc url=”https://www.youtube.com/watch?v=AoDh1T_0Obg”]

I na koniec

Mam nadzieję, że starczy mi czasu i determinacji na kolejne wpisy o AngularJS. Na chwilę obecną nie jestem w stanie przewidzieć o czym będzie mowa następnym razem. Przewiduję, że natchnie mnie w pewnym momencie na napisanie bardziej praktycznego wpisu. Sam temat jest z pewnością wart uwagi.

AngularJS #5 – ng-repeat

AngularJS



W poprzednim wpisie wprowadziłem Was w tematykę filtrów w AngularJS. Mocno powiązanym elementem jest dyrektywa ngRepeat, która pozwala na iterowanie po kolekcji elementów oraz ich wyświetlanie i / lub filtrowanie.

Wyobraźmy sobie znany pewnie wszystkich mechanizm foreach znany z innych języków programowania. Użycie dyrektywy ng-repeat pozwala właśnie na zbudowanie iteratora dla zdefiniowanej kolekcji elementów. Całość jest banalnie prosta w użyciu, a sama dyrektywa ngRepeat posiada kilka wbudowanych wartości, które możemy użyć w ramach iterowanej kolecji. W oficjalnej dokumentacji AngularJS przedstawiona została tabela opisująca te wartości.

Przykład użycia bez filtrowania:

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/EGW5w/7/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Dzięki tym kilku linijkom w HTML jesteśmy w stanie wyświetlić wszystkie elementy z tablicy $scope.cities zdefiniowanej w kontrolerze.

Przykład użycia z filtrowaniem wyników:

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/EGW5w/9/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

By filtrować wylistowane wyniki wystarczyło dodać nowe pole input nad listą oraz zdefiniować dla niego dyrektywę ng-model. Następnie w wywołaniu ng-repeat dodałem parametr filter z nazwą ng-model zdefiniowanego na pola input. Całość działa bez największych problemów. Czegóż chcieć więcej?

Odpowiednik http://egghead.io

[youtube_sc url=”https://www.youtube.com/watch?v=bLohP9mh8ks”]

AngularJS #1 – Wprowadzenie

AngularJS



AngularJS to framework JavaScript stworzony przez inżynierów z Google. Służy on do szybkiego i łatwego budowania aplikacji internetowych, tak zwanych – single app. Model oparty o MVW (Model – View – Whatever) pozwala pogodzić idee JavaScript i modelu MVC. Postaram się to Wam pokazać w kolejnych wpisach.

AngularJS – Superheroic JavaScript MVW Framework

HTML na sterydach

To co wyróżnia AngularJS spośród innych tego typu frameworków, to własny kompilator HTML. Pozwala nam on “nauczyć” naszego HTML nowych sztuczek, zachowań i dodać mu kilka funkcjonalności. W ten sposób jesteśmy w stanie zbudować dynamiczną web aplikację.

Two Way Data-Binding

Najlepszym sposobem na oddanie zasady działania będzie oficjalny schemat:

Two Way Data-Binding

Podwójne bindowanie pozwala na automatyczną synchronizację danych, które mamy po stronie widoku, a naszym kontrolerem / modelem danych po stronie JavaScript.

Ciężko opisać ten mechanizm słowami, dlatego zapraszam do pierwszej lekcji kursu na http://egghead.io/ (polecam!), która pokazuje ten element w działaniu.

[youtube_sc url=”https://www.youtube.com/watch?v=Lx7ycjC8qjE”]

AngularJS $scope

$scope to swego rodzaju obiekt, w którym przechowane są dane tworzone przez kontroler. Dane układane są hierarchicznie. Weźmy za przykład następujący kod HTML:

<div ng-controller="ParentController">
	<h1>{{ title }}<h1>

	<div ng-controller="ChildController">
		<h2>{{ heading }}<h2>
	</div>
</div>

Tym samym mamy dwa div-y. Jeden osadzony w środku drugiego. Za ten zewnętrzny odpowiada kontroler ParentController, który wyglądać może tak:

var ParentController = function ($scope) {
	$scope.title = 'Parent Controller';
};

ChildController w środku prezentuje się następująco:

var ChildController = function ($scope) {
	$scope.heading = 'Child Controller';
};

Oba jako argument funkcji przyjmują zmienną $scope, czyli specjalny obiekt AngularJS, który wstrzykiwany jest do funkcji na etapie kompilacji. O tyle, o ile ChildController w swoim $scope posiada zmienną title z kontrolera ParentController, o tyle ParentController nie ma dostępu do zmiennej heading z ChildControllera ponieważ jest jego kontrolerem nadrzędnym.

O samym $scope można pisać sporo. Jestem pewien, że kolejne wpisy przybliżą ten element frameworka znacznie lepiej.

Dyrektywa?!

Dyrektywa, to właśnie sposób na nauczenie HTML nowych sztuczek. Jedną już poznaliśmy. Mam tutaj na myśli ng-controller, który wygląda jak zwykły atrybut HTML. AngularJS podczas fazy interpretacji kodu HTML rozpoznaje takie elementy i buduje dla nich $scope, w który to wstrzykiwany jest kod odpowiedzialny za obsługę dyrektywy. Tym samym jesteśmy w stanie tworzyć dyrektywy, które mogą być atrybutami istniejących tagów HTML, nazwami klas CSS, nowymi tagami HTML, czy zwykłymi komentarzami. Na początku może to wyglądać na nieco skomplikowane i niepotrzebne, ale zapewniam, że po bliższym poznaniu zrobi się wręcz niezbędnym i ulubionym sposobem na tworzenie logiki biznesowej dla naszego HTML.

Hello {{ name }}!

Czas na pierwszy swoisty przykład działania. Posłużę się w tym celu JSFiddle, w którym to ładuję kod AngularJS, tworzę bardzo prosty HTML i dodaję kontroler ApplicationCtrl, gdzie ustawiam wartość dla zmiennej name. W samym HTML pobieram (binduję) wartość zmiennej {{ name }} ze $scope naszego kontrolera. Ot co, proste Hello World!

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/snL46/7/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Podsumowanie

Rosnąca popularność AngularJS nie jest przypadkiem. AngularJS daje nam możliwość modułowej budowy naszej aplikacji, która testowana może być w równie łatwy sposób, co samo pisanie kodu biznesowego. AngularJS nie trzeba się uczyć od razu w całości. Próg wejścia jest stosunkowo niski, a w miarę rozbudowy naszego projektu / aplikacji możemy poznawać kolejne funkcjonalności i elementy frameworka. Nasza aplikacja otrzymuje tchnienie życia, a my możemy się świetnie bawić pisząc kod JavaScript. Tak, JavaScript.

Zachęcam do nauki i do czytania kolejnych wpisów.