Podstawy aplikacji ember.js - zależności, szablony, komponenty

Mając szkielet aplikacji emberowej można zająć się tworzeniem prawdziwej aplikacji. Na przykład wybrałem szablon AdminLTE pod aplikację w stylu panelu admina, czy panelu statystyk.

AdminLTE to darmowy szablon admina. Robiąc to po staremu pobralibyśmy szablon, wrzucili style, grafiki i skrypty do statyki i użyli w aplikacji Django, czy ember. W tym przypadku można to zrobić też inaczej. AdminLTE dostępny jest jako pakiet npm i można zainstalować go bowerem jako zależność naszej aplikacji:

bower install admin-lte

W chwili pisania artykułu zainstalowała się wersja 2.3.2 tak więc dodałem tą zależność do bower.json:

"admin-lte": "2.3.2"

Dzięki temu inny programista, czy system budowania dostanie tą zależność wykonując bower install w katalogu projektu.

W dokumentacji AdminaLTE znajdziemy nazwy bazowych klas stosowanych przez górną i boczną belkę oraz blok treści. Możemy użyć ich w szablonie application.hbs - głównym szablonie aplikacji emberowej:

<div class="wrapper">
    <header class="main-header">header</header>
    <aside class="sidebar-wrapper">aside</aside>
    <section class="content-wrapper">{{outlet}}</section>
</div>

outlet to zmienna szablonów w emberze - oznacza miejsce w który wstawiona zostanie treść szablonu danego routera (strony). Tak więc w tym przypadku każda ze stron będzie miała własną zawartość bloku treści.

Gdy wrzucimy kod HTML zauważymy że nie mamy podpiętych plików CSS adminaLTE. Musimy dodać je do ember-cli-build.js:

  app.import('bower_components/admin-lte/bootstrap/css/bootstrap.css');
  app.import('bower_components/admin-lte/dist/css/AdminLTE.css');
  app.import('bower_components/admin-lte/dist/css/skins/_all-skins.css');
  app.import('bower_components/admin-lte/plugins/jQuery/jQuery-2.1.4.min.js');
  app.import('bower_components/admin-lte/bootstrap/js/bootstrap.min.js');
  app.import('bower_components/admin-lte/dist/js/app.min.js');

Są to ścieżki do plików AdminLTE zainstalowanego przez Bowera. Te same pliki znajdziemy użyte w demie AdminaLTE. Dla dodatkowych widżetów JS będziemy musieli dodać tutaj dodatkowe pliki, ale na razie to wystarczy. Z tymi plikami układ strony będzie już poprawny, ale jeszcze nie będzie wyglądał tak fajnie jak w demie. Musimy dodać nieco kodu HTML z przykładu admina (application.hbs):

<html>
<head>

</head>
<body class="skin-blue">
<div class="wrapper">
    <header class="main-header">
        <a href="/" class="logo">
          AdminLTE
        </a>
        <nav class="navbar navbar-static-top" role="navigation">
          <a role="button" data-toggle="offcanvas" class="sidebar-toggle" href="#">
            <span class="sr-only">Toggle navigation</span>
        </a>
        </nav>
    </header>
    <nav class="sidebar-wrapper">
        <div class="main-sidebar">
            <div class="sidebar">
                <ul class="sidebar-menu">
                    <li><a href="#"><span>Link</span></a></li>
                    <li><a href="#"><span>Link</span></a></li>
                    <li><a href="#"><span>Link</span></a></li>
                </ul>
            </div>
        </div>
    </nav>
    <section class="content-wrapper">
        {{outlet}}
    </section>
</div>
</body>
</html>
Szkielet AdminaLTE w aplikacji EmberJS

Wariant skórki wybiera się ustawiając klasę taga body. Po dodaniu kodu nagłówka i listy linków strona wygląda już poprawnie. Dodajmy teraz nową podstronę - route:

ember generate route boxes

Stworzy to route/podstronę o URL /boxes. Możemy ją podlinkować w bocznym menu używając odpowiedniego taga:

<li>{{#link-to "boxes"}}<span>boxes demo</span>{{/link-to}}</li>

Generator stworzył nam też szablon boxes.hbs w katalogu app/templates. Idąc za nazwą załóżmy że chcemy wyświetlić trzy kolorowe boksy ze statystykami. Szkielet grida Boostrapa wygląda tak:

<div class="row">
  <div class="col-xs-6 col-md-4">
      box
  </div>
  <div class="col-xs-6 col-md-4">
      box
  </div>
  <div class="col-xs-6 col-md-4">
      box
  </div>
</div>

Moglibyśmy teraz w każdy boks wkleić kod HTML boksa, ale to byłaby duplikacja kodu. Można to zrobić lepiej za pomocą komponentu:

ember generate component statistics-box

Co stworzy nam komponent. Jego szablon to app/templates/components/statistics-box.hbs, do którego możemy wrzucić kod HTML boksa:

<div class="info-box">
  <span class="info-box-icon bg-aqua"><i class="ion ion-ios-gear-outline"></i></span>
  <div class="info-box-content">
    <span class="info-box-text">CPU Traffic</span>
    <span class="info-box-number">90<small>%</small></span>
  </div>
</div>

Możemy teraz użyć komponentu w szablonie boxes.hbs:

<div class="row">
  <div class="col-xs-6 col-md-4">
      {{statistics-box}}
  </div>
  <div class="col-xs-6 col-md-4">
      {{statistics-box}}
  </div>
  <div class="col-xs-6 col-md-4">
      {{statistics-box}}
  </div>
</div>
Szkielet AdminaLTE w aplikacji EmberJS

Trzy boksy się wyświetlą, ale w porównaniu do dema nie widać ikony. Pochodzą one z zewnętrznych pakietu font-awesome oraz ionicons. Moglibyśmy pobrać paczkę ZIP, wrzucić rozpakowany katalog do katalogu public i podlinkować style czcionek w application.hbs:

<link rel="stylesheet" href="vendor/font-awesome-4.4.0/css/font-awesome.min.css">

Gdzie vendor/font-awesome-4.4.0 to ścieżka względem katalogu public.

Z drugiej strony font-awesome jest dostępny jako pakiet bowera więc możemy dodać go do zależności zamiast wrzucać do projektu i naszego repozytorium. Do bower.json dodajemy zależność:

"components-font-awesome": "4.4.0"

I instalujemy poleceniem bower install. Teraz w ember-cli-build.js musimy podać ścieżkę do plik CSS jak i do plików czcionek co wymaga nieco więcej konfiguracji:

  app.import('bower_components/components-font-awesome/css/font-awesome.min.css');
  app.import('bower_components/components-font-awesome/fonts/FontAwesome.otf', {
      destDir: 'fonts'
  });
  app.import('bower_components/components-font-awesome/fonts/fontawesome-webfont.eot', {
      destDir: 'fonts'
  });
  app.import('bower_components/components-font-awesome/fonts/fontawesome-webfont.svg', {
      destDir: 'fonts'
  });
  app.import('bower_components/components-font-awesome/fonts/fontawesome-webfont.ttf', {
      destDir: 'fonts'
  });
  app.import('bower_components/components-font-awesome/fonts/fontawesome-webfont.woff', {
      destDir: 'fonts'
  });
  app.import('bower_components/components-font-awesome/fonts/fontawesome-webfont.woff2', {
      destDir: 'fonts'
  });

Opcja destDir pozwala wybrać katalog do jakiego zostanie wrzucony podany plik. Po dodaniu pliku CSS można w konsoli JS zobaczyć że chce użyć plików czcionek z podkatalogu fonts, ale ich nie znajduje - więc umieszczenie ich w oczekiwanej przez plik CSS ścieżce załatwia sprawę.

Tak samo wygląda sprawa dla ionicons:

"ionicons": "2.0.1"
app.import('bower_components/ionicons/css/ionicons.min.css');
  app.import('bower_components/ionicons/fonts/ionicons.eot', {
      destDir: 'fonts'
  });
  app.import('bower_components/ionicons/fonts/ionicons.svg', {
      destDir: 'fonts'
  });
  app.import('bower_components/ionicons/fonts/ionicons.ttf', {
      destDir: 'fonts'
  });
  app.import('bower_components/ionicons/fonts/ionicons.woff', {
      destDir: 'fonts'
  });

Po dodaniu czcionek ikony boksów powinny być już widoczne (czasami trzeba zrestartować ember server by zauważył nowe pliki):

Ikony awesome i ionicons

Gdy mamy działający styl komponentu trzeba nadać mu trochę dynamizmu. W aplikacji takie komponenty wykorzystywalibyśmy wiele razy do wyświetlania różnych danych. Do komponentu można przekazywać dane pod zmiennymi, które będą dostępne wewnątrz niego. Tak więc zmieńmy kod szablonu komponentu (statistics-box.hbs) na taki:

<div class="info-box">
  <span class="info-box-icon bg-{{color}}"><i class="{{icon}}"></i></span>
  <div class="info-box-content">
    <span class="info-box-text">{{name}}</span>
    <span class="info-box-number">{{yield}}</span>
  </div>
</div>

Nie licząc yield wszystko to są zmienne jakie stworzyliśmy na potrzeby komponentu - styl koloru, ikony, nazwa bloku. Tag yield wstawia treść z komponentu, który jest używany blokowo, więc szablon, w którym używamy tego komponentu (boxes.hbs) modyfikujemy tak by wykorzystać te zmienne i blokowy zapis:

<div class="row">
  <div class="col-xs-6 col-md-4">
      {{#statistics-box name="Foo Bar" color="aqua" icon="fa fa-github-alt"}}100%{{/statistics-box}}
  </div>
  <div class="col-xs-6 col-md-4">
      {{#statistics-box name="Rebels" color="yellow" icon="fa fa-rebel"}}1210{{/statistics-box}}
  </div>
  <div class="col-xs-6 col-md-4">
      {{#statistics-box name="Stormtroopers" color="green" icon="fa fa-empire"}}11{{/statistics-box}}
  </div>
</div>

Lista ikon (nazw klas) dostępna jest w dokumentacji adminaLTE. Po tych zmianach dostajemy coś takiego:

Ikony awesome i ionicons

Komponenty zastąpiły widoku ze starych wersji embera, a niebawem dobiorą się do kontrolerów. Mogą służyć do prostych celów jak generowanie widżetów zaprezentowanych powyżej, czy operować także na danych z modeli - pobierać, modyfikować, zapisywać. Jeden komponent powinien być jednym widżetem, jedną funkcjonalnością. Nie powinniśmy robić ogromnych komponentów o wielu odpowiedzialnościach. Zawsze komponent może wykorzystać inne komponenty. Tego typu przykłady postaram się zaprezentować w kolejnych częściach przewodnika po ember.js.

Podsumowanie

W tej części poradnika zaprezentowałem jak obsługuje się zależności w emberze i jak wykorzystuje się je w projekcie. Stworzyliśmy także pierwszy komponent - budulec, na bazie którego tworzyć się aplikacje w emberze.

Jak na razie nie użyliśmy żadnych modeli, danych zewnętrznych, czy nawet Pythonowego backendu. To będzie w kolejnych artykułach z serii.

RkBlog

Programowanie Sieciowe, 11 November 2015

Comment article
Comment article RkBlog main page Search RSS Contact