Jakiś czas temu pisałem tutaj na blogu na temat różnego rodzaju frameworków JavaScript. Dla przypomnienia, porównałem w nim takie frameworki jak BackboneJS, EmberJS oraz AngularJS. Myślę, że tematyka “frameworkowa” będzie się na łamach tego bloga przewijać wielokrotnie i dziś jest jeden z takich właśnie momentów - podstawy AngularJS :) Wpis powstał na podstawie krótkiej prezentacji na temat Angulara (lokalnie u mnie w firmie), w której prezentowałem o co generalnie chodzi w tym narzędziu i tutaj uwaga - są to absolutne podstawowe podstawy jeśli więc na co dzień używasz tego frameworka to ten post raczej nie jest dla Ciebie!

Moduł

Skoro dziś przedstawiam podstawy AngularJS to swój wywód zacząć muszę od zagadnienia modułu. Moduł jest podstawową koncepcją frameworka AngularJS. Modułem może być w zasadzie wszystko: kontrolery, dyrektywy, serwisy itp. itd. Również sama aplikacja to tak na prawdę taki główny moduł. Deklaruje się to mniej więcej tak:

angular.module('AngularExample', ['ngRoute']);

Na powyższym listingu widzimy wywołanie metody module należącej do globalnego obiektu angular. Jako pierwszy parametr podajemy nazwę modułu - u mnie “AngularExample”. Z kolei drugi parametr to lista nazw modułów, od których zależy dany moduł. W przykładzie widzimy tylko jedną zależność od “ngRoute”, która przyda nam się później.

Ok, w ten sposób zdefiniowaliśmy główny moduł aplikacji. Trzeba go teraz powiązać ze stroną internetową - u mnie wygląda to tak:

<!DOCTYPE html>
<html>
    <head lang="en">
        <meta charset="UTF-8">
        <title>Angular Example</title>
        <link rel="stylesheet" href="styles.css" />
        <script src="angular/angular.min.js"></script>
        <script src="angular/angular-route.min.js"></script>
    </head>
    <body data-ng-app="AngularExample">

        <div data-ng-view>
            Loading...
        </div>

        <!-- custom module -->
        <script src="module.js"></script>

        <!-- routes config -->
        <script src="routes.js"></script>

        <!-- custom controller -->
        <script src="controller.js"></script>

    </body>
</html>

To co na razie nas interesuje to zaznaczona linia numer 10. To właśnie w pokazany sposób (atrybut data-ng-app) informujemy Angulara, że wszystko co znajduje się wewnątrz elementu body to nasza aplikacja.

Kontroler

Podstawy AngularJS nie mogą się obejść również bez opisu kontrolerów (jak to zwykle bywa w MVC)… Kontroler jest sercem danej funkcjonalności - odpowiada za pobranie danych i zasilenie nimi modelu. Poniżej prosty przykład jak taki kontroler może wyglądać:

angular.module('AngularExample').controller('exampleController', function ($scope){
    $scope.message = 'Hello world!';
    $scope.list = [
        {
            firstName: 'John',
            lastName: 'Doe'
        },
        {
            firstName: 'Mark',
            lastName: 'Smith'
        },
        {
            firstName: 'James',
            lastName: 'Mole'
        }
    ];
});

Jak widzicie, aby odwołać się do modułu, posługujemy się podobną konstrukcją jak przy jego deklaracji, z tą różnicą, że tym razem nie podajemy listy zależności - to dobra praktyka aby robić to w ten sposób zamiast przypisywać moduł do zmiennej globalnej. Na rzecz modułu wywołujemy funkcję controller, której jako pierwszy parametr podajemy nazwę kontrolera, a jako drugi, funkcję wywołania zwrotnego. Zauważyliście pewnie, że funkcja ta przyjmuje parametr $scope - jest to taka specjalna zmienna, którą można rozpatrywać jako coś w rodzaju modelu. Przypisuje się jej właściwości, które później można “zbindować” do widoku - wszystkie te właściwości sąautomatycznie obserwowalne, więc każda ich zmiana w jednym miejscu na widoku powoduje zmianę także w innym miejscu. Powyżej utworzyłem dwie takie właściwości: message oraz list. Za chwilę pokażę, jak je połączyć z widokiem. Najpierw jednak omówię jeszcze jeden istotny koncept.

Routing

Jeśli wrócilibyście do przykładu, w którym pokazałem jak podpiąć moduł aplikacji do widoku, zauważylibyście też taką konstrukcję:

<div data-ng-view>
    Loading...
</div>

Natomiast wcześniej jeszcze, przy deklaracji modułu, jako zależność przekazałem mu wartość “ngRoute”. Te dwa elementy służą do czegoś co nazywa się “routingiem” - mówiąc prostym językiem jest to coś, co pozwala na wczytanie do kontenera opatrzonego argumentem data-ng-view przygotowanego wcześniej “template’u” HTML w zależności od wpisanego w przeglądarkę adresu URL.

Generalnie więc, jeśli w adresie mamy na przykład: “http://mojastrona.com/#/widok-jeden” to w kontener wczytany zostanie inny kawałek HTML’a niż gdy w adresie wpiszemy (lub klikniemy w link) dla przykładu “http://mojastrona.com/#/widok-dwa”. Te “hashe” w przykładach adresów są nieprzypadkowe - dzięki temu nie nastąpi pełne przeładowanie strony. Zamiast tego zadziała mechanizm “routingu” i wczyta się odpowiedni widok.

Ok. Tyle teorii. Zobaczmy teraz jak skonfigurować taki “routing”:

angular.module('AngularExample').config(['$routeProvider',
    function($routeProvider) {
        $routeProvider
            .when('/', {
                templateUrl: 'example.html',
                controller: 'exampleController'
            }, null)
            .when('/list', {
                templateUrl: 'list.html',
                controller: 'exampleController'
            }, null);
    }]);

Ogólnie rzecz biorąc mamy tutaj do czynienia zkonfiguracją modułu, a konkretniej “routeProvidera” dostarczanego nam wraz z modułem “ngRoute”. Nie będę dokładnie wyjaśniał zawiłości tego modułu. Na potrzeby tego wpisu wystarczy nam tylko tyle, że w powyższym przykładzie zdefiniowałem dwa “routingi”: jeden dla adresu typu “http://…./#/” - dla niego do kontenera wczytana zostanie zawartość pliku example.html i połączona ona zostanie z naszym kontrolerem “exampleController”. Drugi natomiast odpowiada adresowi typu “http://…/#/list” i dla niego wyświetlona zostanie zawartość pliku list.html powiązana również z tym samym kontrolerem. Ok, to teraz wreszcie czas na widoki.

Widoki

W poprzednim akapicie zdefiniowaliśmy “routingi” dla plików example.html oraz list.html. Poniżej zawartość pliku example.html:

<div data-ng-controller="exampleController">
    <p></p>
    <a href="/AngularExample/#list">Show list</a>
</div>

Powyżej widzimy kontener div, z którym wiążemy kontroler “exampleController” za pomocą atrybutu data-ng-controller. W następnej linii mamy do czynienia z “bindowaniem” modelu do widoku. Jak pamiętacie, w kodzie kontrolera przypisałem do zmiennej $scope właściwość message - teraz w widoku, w miejscu `` wyrenderowana zostanie aktualna wartość tej właściwości. W trzeciej linii mamy po prostu link do drugiego ze zdefiniowanych wcześniej “routingów”.

Zobaczmy teraz co kryje się w pliku list.html:

<div data-ng-controller="exampleController">
    <div data-ng-repeat="item in list">
        <h4>User:</h4>
        <p>Fist name: </p>
        <p>Last name: </p>
        <p>Change last name: <input type="text" data-ng-model="item.lastName"></p>
    </div>
    <a href="/AngularExample/#/">Go back</a>
</div>

Tutaj jest trochę ciekawiej. Po pierwsze w drugiej linii przykładu widzimy jak można iterować się po elementach tablicy (data-ng-repeat) - wszystko to co znajduje się wewnątrz tego kontenera wyrenderuje się tyle razy ile jest elementów tablicy list. W liniach 4 i 5 widzimy odwołanie do aktualnego elementu iteracji (aktualny element tablicy znajduje się w zmiennej item). W linii szóstej natomiast, mamy przykład powiązania zmiennej modelu z polem tekstowym - robimy to za pomocą atrybutu data-ng-model. Jeśli użytkownik zmieni wartość pola tekstowego, zmienna modelu zostanie automatycznie zaktualizowana i zmiana ta będzie od razu widoczna wszędzie tam, gdzie ma miejsce “bindowanie” do tej właściwości. I to w zasadzie tyle - wcale nie taki diabeł straszny jak go malują, co nie? ;)

Podstawy AngularJS - podsumowanie

Niestety nie udało mi się wygenerować tego przykładu na codepen.io - okazało się to dla mnie zbyt skomplikowane… pewnie to moje lenistwo bo ludzie robią tam dużo bardziej skomplikowane przykłady ;) Nie zostawiam Was jednak z suchym tekstem… Zamiast tego możecie pobrać ten przykład w formacie *.zip i wypróbować go w domu, do czego zachęcam.

To co dziś pokazałem przedstawia absolutne podstawy AngularJS. Rzecz jasna to tylko ułamek jego możliwości, wystarczy jednak aby zacząć - moim zdaniem przeanalizowanie i poklikanie takiego przykładu powinno pomóc komuś nieobeznanemu z tym frameworkiem w oswojeniu się i zrozumieniu głównych konceptów. Jednocześnie namawiam do zgłębiania tego tematu bo to bardzo fajny framework jest!