angularjs - component header

AngularJS – Component

Wpis ten, będzie związany z komponentami oraz ich implementacją w AngularJS. Przedstawię zalety pisania w oparciu o ten sposób tworzenia aplikacji. Omówię tworzenie komponentów wraz ze wszystkimi możliwościami konfiguracji. A także, utworzymy razem prostą aplikację, służącą do robienia listy zakupów, przykład ten idealnie pokazuje idee tworzenia aplikacji w oparciu o komponenty.

Trochę teorii

Komponenty to uproszczone w tworzeniu dyrektywy (o nich będzie mowa już niedługo). Dyrektywa to znacznik napisany przez osobę tworzącą aplikację, posiadająca własny wygląd oraz przyjmowane atrybuty. Pojawiły się wraz z wydaniem wersji AngularJS 1.5. Aplikacje tworzone za pomocą component przypominają wykorzystywanie Web Components, czy pisania aplikacji w wersji Angular 2.

Zaletami tego rozwiązania są: prosta konfiguracja, tworzenie aplikacji w oparciu o komponenty oraz możliwość prostrzego przejścia na nowsze wersję frameworka.

Obiekt konfiguracyjny składa się z kilku atrybutów, które pokrótce omówię.

bindings – dane przekazane za pomocą atrybutów w kodzie html. Wyróżniamy kilka znaków, które określają typ przekazywanych danych:
@ – oznacza, że w atrybucie zostanie wprowadzony ciąg znaków.
= – wiązanie dwukierunkowe, czyli, gdy wprowadzona dana zostanie zmieniona, to zmiana również nastąpi w miejscu z którego została załadowana do komponentu.
– wiązanie jednokierunkowe, wprowadzane zmiany następują jedynie w obrębie komponentu.
& – oznacza, że przekazana zostanie funkcja.

controller – funkcja, która jest kontrolerem dyrektywy. Domyślnie przy wywołaniu w pliku html korzystamy z $ctrl.

template – wygląd opisany za pomocą ciągu znaków.

templateUrl – wygląd opisany za pomocą dodatkowego pliku.

 

Aplikacja – Lista zakupów

Tym razem utworzymy prostą aplikację do zarządzania listą zakupów. Powstanie z wykorzystaniem komponentów. Zostaną utworzone cztery komponenty. Kod html każdego elementu będzie w osobnym pliku.

Kod pliku index.html

<!DOCTYPE html>
<html lang="pl" ng-app="app">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>AngularJS #9</title>

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
        crossorigin="anonymous">

    <style>
        .panel-default {
            margin-top: 2em;
            padding: 1em;
        }

        .btn {
            width: 100%
        }

        .control-form {
            width: 100%
        }

        .inupt-text {
            margin-bottom: 1em;
            width: 100%;
        }
    </style>

</head>

<body>

    <title-page title="AngularJS #9"></title-page>

    <div class="row">
        <div class="col-md-4 col-md-offset-4 panel panel-default">
            <shop-list></shop-list>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.js" integrity="sha256-tA8y0XqiwnpwmOIl3SGAcFl2RvxHjA8qp0+1uCGmRmg="
        crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
        crossorigin="anonymous"></script>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>

    <script>
        var app = angular.module("app", []);

        app.component('titlePage', {
            templateUrl: 'titlePage.html',
            bindings: {
                title: '@'
            }
        });

        app.component('shopList', {
            templateUrl: 'shopList.html',
            controller: shopListController
        });

        app.component("item", {
            templateUrl: 'item.html',
            bindings: {
                data: '='
            }
        });

        app.component("newItem", {
            templateUrl: 'newItem.html',
            bindings: {
                new: '&'
            },
            controller: newItemController
        });

        function shopListController () {
            var ctrl = this;
            ctrl.list = [{
                name: "apple",
                checked: false
            }, {
                name: "milk",
                checked: true
            }];

            ctrl.addItem = function (name) {
                ctrl.list.push({name: name, checked: false});
            }
        }

        function newItemController () {
            var ctrl = this;
            ctrl.add = function (name) {
                ctrl.new({name: name});
                ctrl.newName = '';
            }
        }
    </script>
</body>

</html>

 

Kod pliku titlePage.html

<h1 class="text-center">{{$ctrl.title}}</h1>

 

Kod pliku shopList.html

<item data="item" ng-repeat="item in $ctrl.list"></item>
<new-item new="$ctrl.addItem(name)"></new-item>

 

Kod pliku item.html

<p>
    <input type="checkbox" ng-model="$ctrl.data.checked" />{{$ctrl.data.name}}
</p>

 

Kod pliku newItem.html

<input class="group-form inupt-text" type="text" ng-model="$ctrl.newName"/>
<button class="btn btn-default" ng-click="$ctrl.add($ctrl.newName)">ADD</button>

 

Wykorzystamy do utworzenia aplikacji szablon strony, który można było zobaczyć we wcześniejszych wpisach. Jak możemy zobaczyć, kod index.html zawiera dyrektywę <title-page> oraz <shop-list>.

Przejdźmy zatem do kody JS. Po utworzeniu modułu AngularJS, przystepujemy do utworzenia pierwszego komponentu, którym jest title-page. Pierwszym parametrem metody component jest nazwa znacznika titlePage (duża litera P oznacza, że w pliku html wywołamy ten komponent używając <title-page>), następnym zaś obiekt konfiguracyjny. Dokładnie, w tym przypadku wykorzystywany jest obiekt, a nie jak w innych przypadkach np. kontrolerach funkcja tworząca. Obiekt dla tego komponentu zawiera dwie właściwości: templateUrl oraz bindings. TemplateUrl wskazuje na plik odpowiedzialny za wygląd utworzonego znacznika, zaś bindings za przyjmowane atrybuty. W tym przypadku mamy jeden atrybut o nazwie title, który przyjmuje ciąg znaków. Komponent ten, odpowiedzialny będzie za wyświetlenie wyśrodkowanej nazwy aplikacji na środku strony.

Kolejną dyrektywą, którą utworzymy jest <shop-list>. Tworzymy ją w podobny sposób jak poprzednią, tym razem jednak dodajemy kontroler jako funkcję o nazwie shopListController napisaną poniżej w kodzie JS. W kodzie kontrolera tworzona jest zmienna odnosząca się do obiektu funkcji ctrl. Do tej zmiennej dodawane jest pole reprezentujące listę zakupów będące tablicą, a także funkcja dodająca do tej tablicy dane. Przechodząc do pliku shopList.html widzimy dwa nowe komponenty: <item> oraz <new-item>. Pierwszy odpowiada za wyświetlanie listy produktów do kupienia, drugi zaś za dodawanie nowych danych do tablicy.

Każdy element listy wyświetlany jest za pomocą przekazanego obiektu do znacznika <item> jako atrybut data. Iteracja po obiektach listy odbywa się za pomocą atrybutu ng-repeat. Przejdźmy teraz do kodu JS odpowiedzialnego za implementację tego komponentu. W obiekcie konfiguracyjnym przekazujemy jedynie obiekt data.

Ostatni komponent, odpowiada za dodawanie elementów do listy. W obiekcie konfiguracyjnym mamy dwie właściwości. Bindings przekazuje funkcję dodającą, natomiast kontroler wskazuje na funkcję newItemController. W kontrolerze następuje utworzenie zmiennej ctrl, do której przypisana jest funkcja add() implementująca przekazaną funkcję przez atrybut. Funkcja add() wywołana jest w pliku newItem.html za pomocą atrybutu ng-click dla przycisku. Jedyne na co musimy zwrócić uwagę to wywołując przekazaną funkcję przez atrybut należy parametr przypisać za pomocą obiektu {nazwa parametru z atrybutu: wartość jaka ma zostać pobrana}.

 

Efekt działania aplikacji

angularjs - component

 

Podsumowanie

W tym wpisie udało się nam omówić działanie komponentów w AngularJS. Sposób ich tworzenia oraz pokazać na przykładzie prostej aplikacji jak wykorzystywać możliwości jakie dają komponenty. Przedstawiony został obiekt konfiguracyjny wraz z polami jakie w nim występują. W kolejnym wpisie, zajmiemy się kolejnymi elementami tego frameworka.

Podziel się ze znajomymi