Dyrektywy w AngularJS

Dyrektywa (directive) jest to template np. połączenie html i angular, który wielokrotnie może być użyty na stronie. Template ten zazwyczaj wczytywany jest z osobnego pliku HTML i niezwykle pomaga w utrzymaniu czytelności kodu, ułatwia testowanie fragmentów kodu i pozwala dzielić aplikację na moduły.
Istnieje wiele wbudowanych dyrektyw, których nie będę tutaj omawiał. Skupię się na omówieniu bardziej skomplikowanej części czyli własnych dyrektyw, której towarzyszy mnogość parametrów.
Przy Angularze główny plik HTML np. index.html jest bardzo odchudzony właśnie dzięki dyrektywom. Zamiast sporych części kodu, która zawiera MENU, FOOTER, itp. wystarczy zapis <my-menu></my-menu><some-footer></some-footer> a cały kod pobierany będzie przez dyrektywę z innego pliku HTML.

Przykładowa deklaracja dyrektywy:

app.directive("myDirective", function() {
    return {
       restrict: "EAC",
       controllerAs: "ctrl",
       controller: "myController",
       template: "{{ctrl.data}}"
    };
});

restrict

Jak zostało pokazane w powyższym przykładzie EACM wskazują Angularowi, w których elementach ma szukać dyrektywy w pliku HTML. Najczęściej używane, są trzy pierwsze metody, a jeżeli używana jest tylko jedna z nich warto w tym miejscu ograniczyć się do niej, żeby Angular miał mniej pracy do wykonania w zakresie poszukiwania dyrektyw.

  • E – dyrektywa jako element <new-list></new-list>
  • A – dyrektywa jako atrybut <div new-list></div>
  • C – dyrektywa jako klasa <div class=”new-list”></div>
  • M – dyrektywa jako komentarz <!- – directive: new-list –>

replace

Instrukcja replace: true zastępuje element, który wywołuje dyrektywę, a klasy oraz style w nim użyte przenoszone są do zaimportowanej dyrektywy

template

Template czyli gotowy kawałek kodu do wstrzyknięcia może być zadeklarowany na dwa sposoby. Pierwszym jest bezpośrednie wpisanie w nim kodu (ma to sens, tylko przy jego małej ilości). Drugi to podanie ścieżki do pliku HTML.

    template: "<div>Adding New HTML code from template</div>"
    templateUrl: "someTemplate.html"

controller

W sytuacji, gdy w dyrektywie nie zostanie określony scope ani controller dyrektywa będzie korzystała z kontrolera, w którym została wywołana. Jest to najprostszy przypadek, jednak problem pojawia się gdy dana dyrektywa zostanie użyta więcej niż jeden raz. W takim wypadku kilka użytych elementów input będzie zmieniało wzajemnie swoje dane.

    controller: "myController" - wskazuje, który (istniejący) kontroler będzie zarządzał kodem
    controllerAs: "ctrl", - umożliwia zmianę nazwy kontrolera (działa jak alias)

scope w deklaracji dyrektywy

    scope: true
    $scope.data = { country: „PL” }; //jest synchronizacja przy scope: true
    $scope.city = "Wrocław"; //brak synchronizacji przy scope: true

Dla opcji scope: true każdy użyty element będzie miał niezależny scope. Przy tej opcji i korzystaniu z jednego kontrolera synchronizacja pomiędzy każdą wywołaną dyrektywą zależeć będzie od tego, jak dane zostały zadeklarowane w kontrolerze. W pierwszym z przypadków (pokazanym wyżej $scope.data) wartość jest zadeklarowana w obiekcie zatem jej zmiana dokona się we wszystkich dyrektywach. W drugim przypadku ($scope.city) na początku wartość również będzie równa, jednak jeżeli jedna z nich zostanie zmieniona np. w elemencie input to tutaj już synchronizacji nie będzie.

Inne możliwości ustawienia scope:

  • scope: {} – tworzy nowy pusty scope nie dziedziczy od nikogo
  • scope: { local: "@keytemp" } – wiązanie jednokierunkowe, do scope podana jest wartość z kontrolera, wewnątrz którego została wywołana dyrektywa
    <div new-list keytemp=”{{ data.country }}” ></div>
  • scope: { local: "=keytemp" } – wiązanie dwukierunkowe, w elemencie HTML brak nawiasów
    <div new-list keytemp=”data.country” ></div>
  • scope: ageFn: "&age" – dodaje do wywoływanej funkcji jeden argument

app.controller("mainCtrl", function($scope) {
    $scope.data = { 
         age: 15 };
         country: „PL” 
    };
    $scope.getAge = function(age) {
        return age = 35;
    }
});

<div new-list age="getAge(data.age)" keytemp="data.country" ></div>
app.controller("myController", function($scope) {
    $scope.data = "some data from controller";
});

app.directive("myDirective", function() {
    return {
       restrict: "EAC",
       controllerAs: "ctrl",
       controller: "myController",
       template: "{{ctrl.data}}"
    };
});