Mustache JS na przykładzie aplikacji Express. Część 2/2.

mustache-js

Poziom zaawansowany

Witamy w II części tutoriala o Mustache JS i Express. Dziś dokończymy dzieło rozpoczęte w części I.

Mustache JS i jQuery w akcji

Dotąd omówiliśmy podstawy pracy z biblioteką Mustache, przygotowaliśmy dane testowe oraz serwer w node.js. Teraz pora na oprogramowanie warstwy front-end.

W naszym przypadku użyjemy:

Bootstrap v3.3.5

jQuery v1.11.3

jQuery Easing v1.3 plugin

Ionicons

mustache JS

Kolejny zatem krok to strona html5, stworzona w oparciu o Bootstrap.

Szczególnie zwróćmy uwagę na 2 elementy – nawigację oraz szablony Mustache.

Wydawnictwo Strefa Kursów

Fragment kodu – top menu z koszykiem na zakupy z boku:


...
<nav class="navbar navbar-default navbar-fixed-top" 
  role="navigation">
  <div class="container">
    <div class="navbar-header page-scroll">
      <button type="button" class="navbar-toggle" 
        data-toggle="collapse" 
        data-target=".navbar-ex1-collapse">
        <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 page-scroll" href="#page-top">
        <span class="logo-text">Code.js Food</span>
      </a>
    </div>

    <div class="collapse navbar-collapse navbar-ex1-collapse">
      <ul class="nav navbar-nav center-nav">
        <li class="hidden">
          <a class="page-scroll" href="#page-top"></a>
        </li>
        <li>
          <a class="page-scroll" href="#pizza">
            <span>
              <i class="ionicons ion-pizza"></i>
              &nbsp;Pizza &nbsp;
            </span>
          </a>
        </li>

      ...
      </ul>
    </div>

    <div class="order-preview">
      <a class="page-scroll" href="#order" title="Ordered items">
        <i class="glyphicon glyphicon-shopping-cart"></i>
        <strong id="order-items-count">(0)</strong>
      </a>
    </div>
  </div>
</nav>
...

Dalej tworzymy poszczególne sekcje.

Działająca aplikacja, pokazująca menu - pozycje wygenerowane przez Mustache JS

Działająca aplikacja, pokazująca menu – pozycje wygenerowane przez Mustache JS

Proszę o przejrzenie pełnego kodu strony index.html, który znajduje się tutaj:

https://github.com/dominik-w/express-food-app/blob/master/index.html

Warto również rzucić okiem na style CSS dostępne tutaj:

https://github.com/dominik-w/express-food-app/blob/master/public/css/app.css

W pliku tym znajdziemy style elementów naszej aplikacji. Warto spojrzeć na media queries, które adaptują front-end dla mniejszych ekranów:

@media(max-width:767px) {
    .center-nav {
        position: relative;
        left: 0.2em;
        width: 98%;
    }

...

Pora na szablon Mustache:

<script id="pizzas-grid" type="text/template">
  <div class="col-xs-4 col-md-4 pizza-col-item">
    <img class="photo-sml" src="{{img}}" title="Details: {{details}}" alt="Pizza">

    <h6 class="pizza-title"><em>{{name}}</em></h6>

    <div class="pizza-price">{{price_format}}</div>

    <div title="Add to cart" class="pizza-add-to-cart">
      <a href="javascript:pizzaApp.addPizzaToCart('{{name}}', '{{price}}', '{{index}}')">
        <i class="glyphicon glyphicon-shopping-cart"></i> (+)
        <span class="cart-added-blinker" id="blinker-{{index}}"> &nbsp;Added to cart</span>
      </a>
    </div>
  </div>
</script>

Tak to wygląda. Mieszamy kod szablonu z placeholderami, w których zostaną podstawione prawdziwe wartości z przetworzonych danych.

Przejdźmy teraz do ożywienia całości.

Plik pizza_app.js – obsługa front-end


jQuery(document).ready(function ($) {

// Handle page scrolling using Easing plugin
    $(function () {
        $('a.page-scroll').bind('click', function (event) {
            var $anch = $(this);
            try {
                $('html, body').stop().animate({
                    scrollTop: $($anch.attr('href')).offset().top
                }, _scroll_speed, 'easeInOutExpo');
            } catch (e) {
                ;
            }
            event.preventDefault();
        });
    });

// Load the initial data
    pizzaApp.loadPizza();
    pizzaApp.loadDrinks();

});

Poza kodem korzystającym z jquery.easing, który definiuje animacje przejść między sekcjami, wywoływane są także metody wczytujące dane naszego menu z jedzeniem i drinkami. Zaimplementujemy je więc.

Wczytywanie danych menu via AJAX:

pizzaApp.loadPizza = function () {
    var url = _app_host + '/pizzas/';

    $.get(url, function () {
        // processData: false
    }).done(function (data) {
        pizzaApp.cb_loadPizza(data);
    }).fail(function () {
        $('#pizzas-items').append("A problem: cannot retrieve menu items.");
    });
};

// callback
pizzaApp.cb_loadPizza = function (data) {
    // data = JSON.parse(data);
    if (data.error > 0) {
        alert(data.msg);

        return false;
    }

    var template = $('#pizzas-grid').html();

    try {
        for (var i = 0; i < data.length; ++i) {
            var obj = data[i];

            // add extra fields - index and formatted price
            obj.index = i;
            obj.price_format = _currency + ' ' + obj.price.toFixed(2);

            var html = Mustache.to_html(template, obj);
            $('#pizzas-items').append(html);
        }
    } catch (e) {
        console.error(e);
    }
};

Właśnie w funkcji callback dzieje się cała magia z użyciem Mustache JS. Następuje przetwarzanie szablonu, który zdefiniowaliśmy w skrypcie o ID „pizzas-grid”.

Następuje wczytanie szablonu:

var template = $('#pizzas-grid').html();

a dalej wygenerowanie HTML wyjściowego i dodanie go do strony:

var html = Mustache.to_html(template, obj);
$('#pizzas-items').append(html);

Pomiędzy generowane i wstawiane są dodatkowe, przydatne dane.

Sytuacja dla drinków jest podobna, z tym że pracujemy z osobnymi szablonami – dla każdej kategorii napoju.

Pełny kod dostępny na GitHubie:

https://github.com/dominik-w/express-food-app/blob/master/public/js/pizza_app.js

Znajdziemy tam kolejne metody, takie jak obsługa dodania pozycji z menu do koszyka:


pizzaApp.addPizzaToCart = function (name, price, idx) {

    var pizzaObj = {name: name, price: parseFloat(price)};
    shoppingCartPizzas.push(pizzaObj);

    // update items counter
    var count = shoppingCartPizzas.length + shoppingCartDrinks.length;
    $('#order-items-count').text('(' + count + ')');

    // update data in "Your order" area
    pizzaApp.rebuildOrderArea();

    // some effects on dynamic elements
    $('#blinker-' + idx).show('slow').delay(800).fadeOut(200);
    $('.order-preview').fadeTo('fast', 0.1).fadeTo('fast', 1.0);

};

To prosty kod, który dodaje sformatowane dane do wirtualnego koszyka i odświeża wyświetlane informacje. Wszystko z prostymi efektami animacji.

Dalej zdefiniowano walidację, która w przypadku wyniku pozytywnego, wywołuje metodę pizzaApp.submit() – oto jej kod:

pizzaApp.submit = function () {

    var url = _app_host + '/submit';

    // pack whole order and post to the server
    var output = {
        pizzas: shoppingCartPizzas,
        drinks: shoppingCartDrinks,
        address: shoppingCartAddress,
        total: _total
    };

    var call = $.ajax({
        type: 'POST',
        data: JSON.stringify(output),
        contentType: 'application/json',
        url: url
    });

    // callbacks
    call.fail(function (jqXHR, textStatus) {
        alert("Request failed: " + textStatus);
    });

    call.done(function (data) {
        // console.log(JSON.stringify(data));

        $("#pizza-alert-dialog").modal('show');
        $("#alert-validation-msg").html("<strong>Ready!</strong>");

        // make cleanups
        shoppingCartPizzas.length = 0;
        shoppingCartDrinks.length = 0;
        shoppingCartAddress.length = 0;
        _total = 0.0;

        $('#order-items-count').text('(0)');
        $('#submit-area').hide();
        $('#order-items').html('No items yet.');
    });

};

Całe zamówienie użytkownika zostaje wysłane metodą POST, poprzez AJAX, do serwera w node.js, który napisaliśmy w poprzedniej części.

Przykładowe zamówienie wyświetlone w konsoli

Przykładowe zamówienie wyświetlone w konsoli

I to wszystko. Możemy jeść:

$ npm start

Polecam także dobry tutorial o samym Mustache JS:

http://coenraets.org/blog/2011/12/tutorial-html-templates-with-mustache-js

Z kolei cały projekt jest dostępny na GitHub:

https://github.com/dominik-w/express-food-app


Podsumowanie

Gorąco zachęcam do analizy całego kodu aplikacji. Mam nadzieję że ukaże on piękno i użyteczność nie tylko node.js i frameworku Express, ale także samej biblioteki Mustache JS, dzięki której nie musieliśmy programować ręcznie całego żmudnego przetwarzania danych wejściowych.

Dziękuję za uwagę i miłego programowania!

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem. Bloger 🙂 Anty-lewak 🙂 Pasjonat programowania, nowych technologii, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0Share on Tumblr0Digg thisEmail this to someonePin on Pinterest0
Możesz skomentować leave a response, lub podać trackback z własnej strony.