Aurelia.io #1 – Wprowadzenie

Jak mawia stare, chińskie porzekadło „Jeśli czegoś możesz być w życiu pewien, to że w nowym roku pojawi się kolejny framework javascript”. Nie inaczej będzie w 2016. W zasadzie dla większości wybór jest oczywisty – Angular 2.0 , który obecnie jest już dostępny w wersji beta. Twórcą zespołu wytwórczego był Rob Eisenberg – twórca durandala. Jednak współpraca szybko została zakończona, ponieważ Rob miał zupełnie odmienne zdanie w wielu kwestiach projektowych. Nie myśląc więc długo zabrał się do tworzenia swojego frameworku, któremu przeświecał jeden cel „mało kodu, dużo możliwości”. Tak oto narodziła się Aurelia. Czy wraz ze swoją ekipą Rob dopiął swego i to właśnie on zatriumfuje w 2016 roku ? Przekonajmy się !

Chciałbym jeszcze zaznaczyć ,że przykłady które zostaną przedstawione napisane zostały w TypeScript. Jeżeli temat Was zaciekawi, możecie pisać swoje aplikacje w ES2015 oraz ES2016. I jeszcze jedno 😀 O ile kod będę tłumacz dość dokładnie, o tyle nie będę za bardzo rozwodził się nad tym co to MV*, co wchodzi w jego skład i co za co jest odpowiedzialne. Jeżeli temat jest dla kogoś z Was zupełnie nowy, to zachęcam najpierw do zapoznania się z nim, żebyście nie poczuli się zagubieni. Dobra, a teraz do rzeczy.

Pierwszą rzeczą jaką należy zrobić jest pobranie paczki dla TypeScript (lub ES) z oficjalnej strony, którą zapodam o tu. Zawiera ona sam framework, pliki konfiguracyjne oraz przygotowany widok + ViewModel, tak aby od razu móc zacząć zabawę. Folder należy otworzyć dowolnym IDE, które obsługuje TypeScript, w moim przypadku będzie to Visual Studio 2015. Klikamy w File -> Open -> WebSite, po czym wybieramy katalog z projektem. Przejdźmy teraz do omówienia trzech istotnych dla nas plików tj. widok główny aplikacji (index.html), moduł główny (app.js) oraz jego widok (app.html).

<!--index.html-->
<!DOCTYPE html>
<html>
  <head>
    <title>Aurelia</title>
    <link rel="stylesheet" href="styles/styles.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body aurelia-app>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
      SystemJS.import('aurelia-bootstrapper');
    </script>
  </body>
</html>

Omówmy pokrótce co tu się dzieje. W linii 11 załadowany zostaje system.js – jest to module lodader, z którego korzysta Aurelia. Poniżej znajduje się config.js, czyli plik konfiguracyjny dla loadera, który jest zintegrowany z jspm (JavaScript Package Manager). Dalej zaimportowany zostaje bootstraper, który po uruchomieniu zacznie „przeglądać” index.html w poszukiwaniu atrybutów Aurelii. Po odnalezieniu atrybutu aurelia-app, bootstraper będzie wiedział ,że uruchomiona ma zostać nowa aplikacja.W tym celu będzie próbował załadować moduł główny (app.ts) wraz z widokiem (app.html). Zobaczmy jak wyglądają:

<!--app.html-->
<template>
    <h1>${message}</h1>
</template>
//app.ts
export class App {
    message: string = 'Welcome to Aurelia!';
}

No cóż, kodu nie ma za wiele. W klasie typescript-owej App zadeklarowano jedną zmienną typu string – message, która zostaje wyświetlona w widoku poprzez interpolację stringa, czyli ${message}.

Ok, mamy niby wszystko co jest potrzebne do uruchomienia naszej aplikacji. Potrzebujemy jeszcze serwera, na którym „postawimy” naszą aplikację. Najszybszym sposobem  będzie instalacja http-server, który możecie pobrać poprzez wcześniej wspomniany jspm. W tym celu w konsoli (ja osobiście używam babuna), wpisujemy :

jspm install http-server -g

 Po zakończeniu instalacji, możemy w końcu uruchomić naszą aplikację. Robimy to komendą:

http-server -o

Jeżeli wszystko poszło po naszej myśli, efekt powinien być następujący:

sample1

 So far, so good… ok, przejdźmy do „właściwej” prezentacji możliwości Aurelii. W tym celu zamieńmy kod app.ts na następujący:

export class App {

    books: Book[];
    newBook: Book;
 
    constructor() {
        this.books = [];
        this.newBook = new Book();
    }
 
    activate() {
        alert("Hello from the other siiiiide !");
    }
    
    addNewBook(): void {
        this.books.push(this.newBook);
        this.newBook = new Book();
    }

    deleteBook(book: Book): void {
        this.books.splice(this.books.indexOf(book), 1);
    }

    changeBookAvailability(book: Book): void {
        book.isAvailable = !book.isAvailable;
    }
}

export class Book {
    title: string;
    author: string;
    isAvailable: boolean;

    constructor() {
        this.isAvailable = true;
    }
}

a widok, na:

<template>
    <table>
        <thead>
        <tr>
            <th>Title</th>
            <th>Author</th>
            <th>Change availability</th>
            <th>Delete</th>
        </tr>
        </thead>
        <tbody>
        <tr repeat.for="book of books" class="${book.isAvailable ? 'green-background': ''}">
            <td> ${book.title}</td>
            <td> ${book.author}</td>
            <td>
                <button click.trigger="changeBookAvailability(book)">
                    <span if.bind="book.isAvailable">Set unavailable</span>
                    <span if.bind="!book.isAvailable">Set available</span>
                </button>
            </td>
            <td>
                <button click.trigger="deleteBook(book)">X</button>
            </td>
        </tr>
        <tr>
            <td>
                <input value.bind="newBook.title"/>
            </td>
            <td>
                <input value.bind="newBook.author"/>
            </td>
            <td>
                <button click.trigger="addNewBook()">Add</button>
            </td>
        </tr>
        </tbody>
    </table>    
</template>

Klasa App się troche rozrosła, ale bez obaw, nie dzieje się tu żadne rocket science. Po pierwsze mamy teraz dwie zmienne: pierwszą jest tablica obiektów Book (ta klasa znajduje się w app , żeby nie wprowadzać zbędnego zamieszania z milionem plików), oraz obiekt newBook – również typu Book. W konstruktorze klasy obie zmienne zostają zainicjowane. Dalej widzimy funkcję activate. Jest to jedna z 4 dostępnych przez aurelię funkcji związanych z cyklem życia widoku. Są to:

  • canActivate  (może zwrócić boolean lub promise booleana)
  • activate 
  • canDeactivate  (może zwrócić boolean lub promise booleana)
  • deactivate

Daje nam to duże możliwości implementacji różnych zachowań np. potwierdzenie ukończenia 18 lat przed przejściem na widok zawierający listę alkoholi w sklepie 😉 Dalej w klasie App znajdują się 3 funkcje odpowiedzialne kolejno za: dodanie nowej książki do tablicy books, usunięcie książki oraz zmianę jej dostępności, którą reprezentuje flaga isAvailabe.

Widok app.html zawiera teraz tabelę, która posiada cztery kolumny: tytuł książki, autor, zmień dostępność, usuń. Omówmy teraz fragment:

<tr repeat.for="book of books" class="${book.isAvailable ? 'green-background': ''}">
    <td> ${book.title}</td>
    <td> ${book.author}</td>
    <td>
        <button click.trigger="changeBookAvailability(book)">
            <span if.bind="book.isAvailable">Set unavailable</span>
            <span if.bind="!book.isAvailable">Set available</span>
        </button>
    </td>
    <td>
        <button click.trigger="deleteBook(book)">X</button>
    </td>
</tr>

W elemencie <tr> przedstawiłem użycie repeatera Aureli – repeat.for. Będzie on iterował po tablicy books, tworząc dla każdego elementu wiersz  oraz zmienną lokalną book, która jest dostępna dla wszystkich dzieci elementu <tr>. Mówiąc prościej book reprezentuje jedną książkę z tablicy. W tej samej linii przedstawiłem także w jaki sposób możemy dynamicznie zarządzać klasami CSS poszczególnych elementów DOM. W tym przypadku jeżeli nasz książka będzie dostępna to naszemu elementowi table row nadamy klasę green-background, która (jak nie trudno się domyślić) zmieni kolor wiersza na zielony. Kolejne 2 linie poprzez interpolację stringa, wypiszą w pierwszych dwóch kolumnach wiersza kolejno: tytuł książki oraz autora. Kolumna trzecia każdego wiersza zawierać ma przycisk, po kliknięciu którego użytkownik będzie mógł zmienić dostępność książki. W tym celu element button zawiera kolejny, nowy atrybut – click.trigger. W środku wyrażenia atrybutu, możemy wywołać funkcję, która została zaimplementowana w naszym ViewModelu, lub napisać ją bezpośrednio w widoku. W tym przypadku wyrażenie:

click.trigger=”book.isAvailable = !book.isAvailable”

byłoby ekwiwalentne do wywołania funkcji changeBookAvailability. Wyjadacze Angulara lub Knockouta zauważą także jedną rzecz. Wywoływanie metod ViewModelu w środku repeat.for nie wymaga odwoływania się poprzez $parent. W środku elementu button zawarłem dwa elementy span, z których każdy posiada atrybut if.bindJego użycie spowoduje, że element który go zawiera zostanie dodany do DOM, tylko w przypadku spełnienia warunku. Jest to więc odpowiednki angularowego ng-if, a nie ng-show. W związku z tym nasz przycisk będzie miał różną etykietę, zależnie od jego dostępności. Czwarta kolumna wiersza to przycisk, który usunie nasz element z tablicy books, poprzez wywołanie deleteBook(book).

Oprócz wierszy z poszczególnymi książkami, nasza tabela posiada także dodatkowy wiersz, który zostanie wyświetlony na końcu i będzie służył do dodawania nowych pozycji:

<tr>
    <td>
        <input value.bind="newBook.title" />
    </td>
    <td>
        <input value.bind="newBook.author" />
    </td>
    <td>
        <button click.trigger="addNewBook()">Add</button>
    </td>
</tr>

Wiersz zawiera dwa pola tekstowe, które zostały bindują wpisany do nich tekst do properties-ów obiektu newBook poprzez value.bind. W tym miejscu warto dodać (choć powinienem w zasadzie napisać o tym na początku), że w przeciwieństwie do Angulara 2.0, Aurelia wykorzystuje two-way data bind. Oznacza to, że zmiany na widoku są synchronizowane z ViewModelem oraz zmiany w ViewModelu są synchronizowane w widoku. 

Ok, zobaczmy jak działa nasza aplikacja !

sample2

Metoda activate faktycznie działa, not badTak zaź prezentuje się nasz widok app.html.

sample3

Jeżeli chcielibyście żebym wrzucił kod na Githuba, napiszcie o tym w komentarzach. Następne wpisy z tego cyklu poświęcę na omówienie nawigacji, wykonywaniu żądań AJAX poprzez aurelia-fetch-client oraz bundlingowi. Zachęcam również do odwiedzenia strony poświęconej Aureli. Znajdziecie tam 30 min filmik objaśniający podstawy tego frameworku oraz  dokumentację. Jeżeli chcecie być na bieżąco z newsami, to odwiedźcie także blog Roba Eisenberga. Jest tam sporo wpisów dotyczących samego frameworku, updatów oraz bezpośredniego porównania kodu Aureli z Angularem 2.0. Co do tego ostatniego to pamiętajcie… każdy ojciec najbardziej chwali swoje dziecko 😉

 

 

You may also like...

  • Mirek Kedzierski

    Super. Trafiłem na blog który zmiksował to co chce się uczyć Aurelia oraz C# – CORE. Mam pytanko. Właśnie chodzę na kurs front-enda mamy do zrobienia projekt webowy. Chciałem zabłysnąć i dodać coś czego się nie uczymy – frameworka. Czy szybko można nauczyć się aurelii do prostego projektu???. Nie wiele oczekuje.