
Dziś stworzymy aplikację Web w oparciu o wspaniałe narzędzia – html5, Bootstrap, jQuery, Mustache JS i Express. Krok po kroku. Użyjemy także ionicons do wyświetlenia ikon.
Mustache JS
Nie każdy (w tym ja) przepada za systemami szablonów np. w PHP. Tutaj jednak sprawa wygląda znacznie inaczej. Mustache JS to implementacja dla języka JavaScript mini-engine szablonów Mustache (logic-less), który powstał dla wielu różnych języków programowania: http://mustache.github.io.
Dlaczego Mustache (i podobne biblioteki) są fajne?
Przede wszystkim mając dane np. pochodzące z obiektów JSON, nie musimy przetwarzać ich ręcznie, co bywa żmudne. Zamiast tego definiujemy niewielki szablon i placeholdery, w których Mustache JS dokona interpolacji. W ten sposób otrzymamy od razu dane zintegrowane z warstwą front-end naszego projektu.
Mając opanowane Mustache JS, oszczędzamy cenny czas i wysiłek. Za chwilę zobaczymy to w praktyce.
Nauka Mustache JS
Warto zacząć od szybkiego wprowadzenia do pracy z tą biblioteką.
1. Szablon definiujemy jak w poniższym przykładzie:
... <body onload="loadUser()"> <div id="target">Loading...</div> <script id="template" type="x-tmpl-mustache"> Hello {{ name }}! </script> </body>
2. Kod JS, który uruchomi magię Mustache:
function loadUser() { var template = $('#template').html(); Mustache.parse(template); // optional, speeds up future uses var rendered = Mustache.render(template, {name: "Luke"}); $('#target').html(rendered); }
Dodatkowy, mały tutorial na Tutsplus:
http://code.tutsplus.com/tutorials/using-the-mustache-template-library–net-14590
Samą bibliotekę z prostą dokumentacją znajdziemy na stronie GitHub projektu:
https://github.com/janl/mustache.js
Mustache JS jest proste, wręcz przyjemne w użyciu, a potrafi znacząco pomóc programiście.
W praktyce – piszemy małą aplikację web
Będzie to mała aplikacja node.js (express) z frontem single page app.
Projekt miał kilka założeń:
Aplikacja on-line do zamawiania jedzenia (pizza, drinks)
– wyświetlanie menu w przejrzysty sposób
– cart – możliwość dodania różnych produktów do koszyka
– każda pizza ma nazwę, cenę, opis i obrazek, napoje zamiast opisu mają typ (soda, beer, rum)
– prosta logika walidacji ceny:
– jeśli użytkownik zamawia tylko napoje, minimalna cena musi wynosić 10 EUR
– jeśli tylko pizza, minimum 30 EUR
– jeśli zarówno jedzenie i napoje – nie ma ceny minimalnej
– użytkownik może podać swoje dane i kliknąć Zamów, aplikacja wyśle zamówienie do serwera (node.js) lub wyświetli komunikat, gdy cena wybranych produktów jest za niska.
Od strony technicznej:
– jQuery (i dowolny plugin, jeśli będzie potrzebny)
– HTML5, Bootstrap
– działanie min. w przeglądarkach FF, Chrome, IE oraz na urządzeniach mobilnych.
Zacznijmy od przygotowania testowych danych wejściowych (jedzenie i napoje)
Dane mogą pochodzić z zewnętrznego źródła w postaci JSON, z API, lub po prostu pobrane z bazy danych i zwrócone via AJAX. Dla uproszczenia, w przykładzie trzymamy je jako tablice obiektów w zewnętrznym pliku JS, który zostanie odczytany przez nasz prosty serwer napisany w node.js:
// pizzas var pizzaList = [ { name: "Margherita", price: 20.99, details: "Lorem ipsum", img: "img/pizza.jpg" }, { name: "Salami", price: 24, details: "Dolor sit amet", img: "img/pizza.jpg" }, { name: "Pizza Super Good, with a long name", price: 44, details: "Great pizza with extra cheese, corn and bacon", img: "img/pizza.jpg" }, { name: "Pepperoni", price: 20, details: "Lorem ipsum Pepperoni", img: "img/pizza.jpg" } ]; // drinks var drinkList = [ { name: "Coca-Cola", price: 5, type: "Soda", img: "img/soda.jpg" }, { name: "Sprite", price: 6, type: "Soda", img: "img/soda.jpg" }, { name: "Pilsner X", price: 6.2, type: "Beer", img: "img/beer.jpg" }, ... { name: "Captain Morgan", price: 7.75, type: "Rum", img: "img/rum.jpg" } ]; // group drinks by type, to display them grouped in "categories" var drinkListByType = {}; for (var i = 0; i < drinkList.length; ++i) { var obj = drinkList[i]; // create the "type key" if missing if (drinkListByType[obj.type] === undefined) { drinkListByType[obj.type] = [obj.type]; } // pack the rest of the data to temporary object var tmp = { name: obj.name, price: obj.price, img: obj.img }; drinkListByType[obj.type].push(tmp); } data = { pizzaList: pizzaList, drinkList: drinkList, drinkListByType: drinkListByType }; module.exports = data;
Warto zwrócić uwagę na kod grupujący drinki w kategorie po parametrze „type”.
Pełny plik tutaj:
https://github.com/dominik-w/express-food-app/blob/master/data.js
Kolejny krok – aplikacja node.js (express)
Najpierw wczytanie potrzebnych modułów oraz zasobów (katalog public/):
var express = require('express'); var app = express(); var path = require('path'); var bodyParser = require('body-parser'); // assets app.use(express.static(__dirname + '/public')); // to process post requests app.use(bodyParser.json());
Moduł bodyParser przyda się do bezbolesnej obsługi żądań typu POST.
W dalszej części kodu następuje wczytanie danych, które uprzednio zdefiniowaliśmy:
// load input test-data var data = require('./data.js'); var pizzaList = data.pizzaList; var drinkList = data.drinkListByType; //console.log(pizzaList); console.log("\n"); console.log(drinkList);
Pora na routes – strona główna oraz podstrony:
// main - at http://localhost:8080 app.get('/', function (req, res) { res.sendFile(path.join(__dirname + '/index.html')); }); // app routes app.get('/pizzas/', function (req, res) { // adding some more pizzas var localList = pizzaList.slice(0); for (var i = 0; i < 30; i++) { localList.push({ name: "Generic Pizza" + (i + 1), price: 30 + i, details: "Lorem ipsum " + (i + 1), img: "img/pizza.jpg" }); } res.json(localList); }); app.get('/drinks/', function (req, res) { res.json(drinkList); });
W przypadku /pizzas/ dodaliśmy prosty kod, który w pętli generuje więcej danych testowych.
Na końcu definiujemy akcję submit (POST) oraz start serwera na porcie 8080:
// submit process app.post('/submit', function (req, res) { // simply show order data; we can save them in DB, // send via API to our pizzeria or whatever we need console.log("New order:\n"); console.log(req.body); res.json({msg: 'Success'}); }); // start the server app.listen(8080);
Obsługa wysłanego zamówienia, która następuje w akcji submit, ogranicza się do wyświetlenia każdego przychodzącego zamówienia w konsoli. W realnej aplikacji możemy zaimplementować tutaj zapisywanie danych w bazie, lub wysyłanie ich via API do pizzerii. Cokolwiek potrzeba 🙂
I tym samym nasz serwer jest gotowy, jednak nie startujmy go jeszcze. Potrzeba jeszcze index.html – strony, którą zaserwujemy.
Tym zajmiemy się już w części II.
Zapraszam!