Aurelia.io #2 – Routing

Część !

Święta wielkanocne trwają w najlepsze, co nie znaczy, że zwalniamy z tempem 🙂 Dziś kolejny wpis z serii o frameworku Aurelia.io. Jeżeli ktoś z Was nie czytał pierwszego wpisu, to serdecznie zapraszam do zapoznania się z nim o tu. Ostatnio udało nam się stworzyć prosty widok, który umożliwiał nam dodawanie książek do listy, oznaczanie stanu dostępności danej książki, oraz usuwania wybranych pozycji. Wyglądał tak:

sample3Może nie wygląda zjawiskowo, ale działa! Co to jednak za aplikacja webowa, która zawiera tylko jeden widok? W dzisiejszym wpisie zaimplementujemy sobie nawigację, która umożliwi nam płynne przechodzenie między kolejnymi ekranami, a wszystko oczywiście jako SPA.

Pierwszą rzeczą jaką zrobimy będzie przeniesienie naszego widoku i view modelu do plików books.html oraz books.ts. Przypomnę, że wcześniej znajdowały się one w plikach o nazwie app. Nie jest to dobra praktyka, ponieważ zwyczajowo przyjęło się, że app jest „sercem” naszej aplikacji klienckiej, wobec czego powinna tam być zawarta jedynie początkowa konfiguracja, a nie logika biznesowa. Kiedy wszystko jest już przeniesione, możemy napisać nasz moduł app. Wygląda następująco:

 


import {RouterConfiguration, Router} from 'aurelia-router';

export class App
{
     router: Router;

     configureRouter(config: RouterConfiguration, router: Router)
     {
     config.title = 'My awesome books';
     config.map([
     { route: ['','books'], name: 'books', moduleId: './books', nav: true, title:'books' }
     ]);

     this.router = router;
     }
}

 

Pierwszą rzeczą jest zaimportowanie RouterConfiguration oraz Router z paczki aurelia-router. Następnie przystąpimy do konfiguracji obiektu router w callbacku configureRouter. Nadajemy tytuł po czym rozpoczynamy mapowanie wszystkich ścieżek w naszej aplikacji. Każdy obiekt, który znajduje się w tablicy przekazywanej do funkcji map zawiera następujące property:

  • route – ścieżka bądź kilka ścieżek, które będą przekierowywać na dany widok. Ważne jest to, aby jeden widok (i tylko jeden) oznaczyć jako domyślny poprzez  podanie pustej ścieżki.
  • name – nazwa mapowania
  • moduleId – nazwa modułu, który zostanie wyrenderowany po przejściu na dany widok. Tu także uwaga. Aurelia domyślnie paruje widok oraz view model, na podstawie nazwy. Tak więc podając w naszym przypadku „books” tak na prawdę mamy na myśli zarówno plik html jak i ts.
  • nav – określa czy widok ma zostać dodany do tablicy navigation
  • title – tytuł strony

Przejdźmy teraz do widoku app.html. Umieścimy tam header, który będzie zawierał linki do widoków, które zostały zamapowane z parametrem nav: true. Kod wygląda następująco:


<template>
  <require from="bootstrap/css/bootstrap.css"></require>
  <require from="font-awesome/css/font-awesome.css"></require>
 
  <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
        <span class="sr-only">Toggle Navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">
        <i class="fa fa-home"></i>
        <span>${router.title}</span>
      </a>
    </div>

 
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
          <a href.bind="row.href">${row.title}</a>
        </li>

      </ul>

 
      <ul class="nav navbar-nav navbar-right">
        <li class="loader" if.bind="router.isNavigating">
          <i class="fa fa-spinner fa-spin fa-2x"></i>
        </li>

      </ul>

    </div>

  </nav>

 
  <div class="page-host">
    <router-view></router-view>
  </div>

</template>

 

Wygląda strasznie,ale tak na prawdę większość to standardowy bootstrap 😛 Nas interesuje wyłącznie to:

 


<li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
     <a href.bind="row.href">${row.title}</a>
</li>


 

Używamy wcześniej poznanej konstrukcji repeat.for w celu wypisania w naszym nagłówku linków do widoków. Teraz możemy sprawdzić czy wszystko działa. Odpalamy:

http-server -o

Naszym oczom ukazuje się taki widok:

 

base-route

So far, so good! Zwróćcie uwagę, że nasz widok został wyrenderowany na domyślnej ścieżce. Gdybyśmy podali {host}/#/books rezultat byłby identyczny. No dobra dodajmy drugi widok jako placeholder, który będzie zawierał jedynie statyczny tekst.

 

ViewModel:

export class FetchViewModel
{    
}

Widok:

<template>
    This is my fetch view !
</template>

Routing:

{ route: 'fetch', name: 'fetch', moduleId: './fetch', nav: true, title: 'fetch' }

 

Odświeżamy stronę iiiii….

 

changed-route

Wygląda na to, że wszystko działa perfekcyjnie 🙂 Czy to wszystko? Teoretycznie tak, w praktyce jednak takie rozwiązanie jest słabe, dlatego że aplikacje webowe zwykle posiadają od kilku do kilkudziesięciu widoków. Oznacza to, że nasz app.ts rozrósł by się bardzo szybko, a przy okazji nie byłby czytelny. Fajnie byłoby zatem mieć możliwość deklarowania osobnych routerów dla różnych obszarów naszej aplikacji np. dla panelu admina, użytkownika, bibliotekarza itd. Na szczęście jest to bardzo proste. W naszym projekcie dodajemy kolejne dwa pliki:

librarian-router.ts:

 

import {Router, RouterConfiguration} from 'aurelia-router'
 
export class LibrarianRouter
{
    router: Router;
    heading = 'Librarian';
 
    configureRouter(config: RouterConfiguration, router: Router)
    {
        config.map([
            { route: ['', 'info'], name: 'info', moduleId: './info', nav: true, title: 'info' },           
        ]);
 
        this.router = router;
    }
}

 

oraz librarian-router.html:

 

<template>
    <router-view></router-view>
</template>

 

Do naszego routera głównego dopisujemy jeszcze:

 

{ route: 'librarian', name: 'librarian', moduleId: './librarian-router', nav: true, title: 'librarian' }

 

Jak widać nasz router bibliotekarza jest tak na prawdę kopią tego z pliku app.ts (ma on oczywiście mapowania swoich widoków). W tym przypadku, także wymagana jest definicja domyślnego widoku w ramach tego routera. Do pliku app.ts dopisaliśmy route librarian. Oznacza to, że wszystkie widoki w ramach „potomka” będą się rozpoczynały od {host}/#/librarian/. Tak jak w poprzednim przypadku, również utworzyłem sobie placeholdera info. Możemy zatem odświeżyć nasz widok:

 

child-router

Nice ! Wszystko działa, tak jak powinno 🙂

Tym optymistycznym akcentem kończymy na dziś. Następny wpis poświęcimy nauce aurelia-fetch-client i postrzelamy sobie trochę REST-ami 🙂 Pojawi się najprawdopodobniej za tydzień.

Nary !

You may also like...