Come creare una HTML5 Mobile App con Backbone.js
Vale la pena pertanto dedicare un’attenzione particolare a questo aspetto.
Quando parliamo di “struttura” di un’applicazione ci riferiamo generalmente al modo in cui vengono organizzati gli elementi che la compongono per poter interagire in modo logico tra di loro. Uno dei pattern (o logiche di organizzazione) più comuni per gestire questi tipi di elementi è il pattern MVC, che basa la sua logica su elementi come viste (view), modelli (model) e collezioni di modelli (collection).
Per approfondire il discorso del pattern MVC e dei framework di cui ci possiamo servire per implementarli al meglio nei nostri progetti, ti consiglio di leggere questo mio articolo :
Ho realizzato oltre 10 applicazioni mobile per i miei clienti, tra cui due progetti personali (Tint Weather App e Dieta SI o NO?) e gran parte di essi sono stati sviluppati con il pattern MVC grazie al Framework Backbone.js abbinato al sistema di templating di Underscore.js, che rendono molto più semplice lavorare con la logica MVC.
Il sistema di templating è una libreria utile per la generazione di markup HTML via client (quindi tramite javascript) combinando dei dati (generalmente in formato JSON) con un template precedentemente compilato. Per avere maggiori informazioni sui sistemi di templating ti consiglio di leggere questo articolo:
In questo articolo ti voglio mostrare come poter realizzare una semplicissima HTML5 Mobile App di base, con Bakcbone.js e delle animazioni CSS3 fluide e performanti, che sfruttano la tecnica dell’accelerazione hardware.
HTML5 Mobile App: l’obiettivo finale
L’obiettivo di questo esperimento è quello di capire il meccanismo di views, models e collection realizzando una semplicissima mobile app che mostra una serie di articoli (o news) su una lista, permetta di aggiungere nuovi articoli programmaticamente, e visualizzare il dettaglio di quelli cliccati nella lista attraverso una view che a comparsa che “entra in scena” grazie ad animazioni CSS3 performanti.
Se se un web designer e vorresti creare la tua applicazione in HTML5, CSS3 e Javascript, forse ti potrebbe interessare il mio e-book: HTML Mobile Accelerato
Trovi DEMO e DOWNLOAD in fondo all’articolo.
Impostazione del documento
Come prima cosa includiamo nel documento le librerie di cui abbiamo bisogno; in particolare all’interno del tag <body> inseriamo il link alla libreria Modernizr, che ci servirà per gestire l’evento di fine animazione (‘endanimation‘) di entrata per la view che mostra i dettagli dell’articolo (per approfondire il discorso di come controllare le animazioni con Modernizr, di consiglio di leggere questo articolo).
1 2 3 |
<head> <script src="js/modernizr.custom.js"></script> </head> |
Proseguiamo includendo prima della chiusura del body (</body>) le altre librerie:
1 2 3 4 5 6 7 |
<!-- Includo gli scripts in fondo alla pagina per velocizzarne il caricamento --> <script src="js/jquery-1.10.2.min.js"></script> <!-- Includiamo anche underscore.js libreria a cui si appoggia backbone.js --> <script src="js/underscore.js"></script> <script src="js/backbone.js"></script> <!-- il file javascript del nostro esperimento--> <script src="js/demo.js"></script> |
Il markup HTML
Vediamo ora come impostare il markup HTML all’interno del documento:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<!-- header della schermata --> <nav class="header"> <a class="left" href="http://www.upcreative.net/iscriviti-alla-newsletter/"><i class="fa fa-globe"></i> Ulteriori guide</a> <button href="#" class="right" data-js="new"><i class="fa fa-plus"></i> Nuovo articolo</button> <h1 class="title">HTML5 Mobile App</h1> </nav> <!-- wrapper il contenuto della lista --> <section class="content"> <h1>Articoli</h1> <ul class="list-group" data-js="list"> </ul> </section> <!-- template che useremo per la view del dettaglio della news: il browser ingnora questa parte di codice, poichè all'interno del tag 'script', ma possiamo invece risalire a questo template da javacript, tramite l'id --> <script type="text/template" id="article-template"> <!-- header della schermata --> <nav class="header"> <button class="right" data-js="close"><i class="fa fa-times-circle"></i></button> <h1 class="title">Dettalgi articolo</h1> </nav> <!-- wrapper per le tab della schermata --> <section class="content"> <h1><%=title%></h1> <p>Data: <%=createdat.getDate()%> / <%=createdat.getMonth()+1%> / <%=createdat.getFullYear()%></p> <article> <!-- il sistema di templating permette di usare i normali controlli Javascript, in modo da poter controllare se è presente o meno l'immagine per l'articolo --> <%if(image){%> <img src="images/<%=image%>" /> <%}%> <%=article%> </article> </section> </script> |
Il codice che si trova all’interno del tag <script type=”text/template” id=”article-template”></script> rappresenta il markup che verrà utilizzato dalla view Backbone.js per la visualizzazione del dettaglio dell’articolo.
Il file Javascript
All’interno del nostro file Javascript (demo.js) come prima cosa salviamo in una variabile il nome dell’evento di fine animazione tramite Modernizr, che verrà utilizzato nella view del dettaglio dell’articolo:
1 2 3 4 5 6 7 8 9 |
/* Grazie a modernizr riprendo il nome dell'evento di fine animazione, che cambia a seconda del browser */ var animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' }; var animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ]; |
Il passo successivo è subito quello di definire il modello (model) Backbone per rappresentare ogni articolo, e la relativa collezione (collection):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//definiamo il modello di un articolo var ArticleModel = Backbone.Model.extend({ // Attributi predefiniti per il modello, che vengono usati //se non passati al momento dell'inizializzazione defaults: { title: "Nessun titolo", article: "", image: false, createdat: new Date() }, //elimina l'istanza del model clear: function() { this.destroy(); } }); //definiamo la collection per la lista degli articoli var ArticleList = Backbone.Collection.extend({ //Impostiamo la referenza al model per questa collection. model: ArticleModel }); |
Fatto ciò passiamo alla definizione delle views che controllano parte dell’interfaccia della nostra HTML5 Mobile App.
In particolare useremo due views:
- la prima, ovvero quella che rappresenta l’app principale, è applicata al body e quindi non deve preoccuparsi di generare nuovo markup HTML, ma di usare quello che abbiamo già inserito nel documento
- la seconda, ovvero quella che rappresenta il pannello che mostra i dettagli dell’articolo, si servirà della parte di markup che abbiamo precedentemente inglobato nel tag <script> con id ‘article-template‘.
View generale
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
//definiamo la view dell'app intera var AppView = Backbone.View.extend({ //a differenza della view di dettaglio dell'articolo, dove impostavamo //quale elemento generare al'inizializzazione, qui comunichiamo //quale oggetto già aesistente utilizzare come markup el: "body", // Deleghiamo gli eventi per la creazione di un nuovo //articolo (il funzionamento ed i selettori sono come quelli di jQuery). events: { //evento //selettore //funzione da eseguire "click [data-js=list] a": "showArticle", "click [data-js=new]": "newArticle" }, // Nella funzione di inizializzazione osserviamo l'evento add //della collection legata a questa view, per richiamare la funzione che // inserisce un nuovo elemento nella lista DOM initialize: function() { //deleghiamo gli eventi della collection alle relative funzioni //evento che si verifica quando un nuovo model viene aggiunto alla collection // l'ultimo parametro è una funzione alla quale viene passata l'istanza del model appena aggiunto // alla collection this.listenTo(this.collection, 'add', this.addArticle); //richiamiamo la funzione che aggiunge gli elementi all alla lista this.addElements(); }, // funzione che crea una nuova view ed aggiunge // l'elemento generato dal render alla lista, in base al modello passato ('articleModel') addArticle: function(articleModel) { //qui, per mostrare una soluzione diversa, generiamo del markup senza ricorrere ad una view a se stante var compiledTemplate = _.template('<a href="#" class="list-group-item"><%=title %> - <span class="text-muted"><%=createdat.getDate()%> / <%=createdat.getMonth()+1%> / <%=createdat.getFullYear()%></span></a></li>'); //aggiungiamo alla lista il markup generato dal sistema di templating, passando i dati in formato Json del model this.$("[data-js=list]").append( compiledTemplate(articleModel.toJSON()) ); }, showArticle: function(e) { e.preventDefault(); var item = $(e.currentTarget), //riprendiamo l'oggetto jQuery del link appena cliccato index = item.index(), //riprendiamo l'indice sulla lista dell'oggetto appena cliccato articleModel = this.collection.at(index); //riprendiamo il model dalla collection degli articoli in base all'indice //creaiamo una nuova istanza della view dell'articolo passando come model quello dell'articolo cliccato var view = new ArticleView({model: articleModel}); //aggiungiamo all'elemento di questa view (quindi il body della pagina) l'oggetto DOM generato dalla funzione di render della view appena istanziata this.$el.append(view.render().el); //richiamiamo il metodo "show()" interno della view appena creata per avviare l'animazione di entrata view.show(); }, // Aggiunge nella lista tutti gli elementi della collection di individui. addElements: function() { this.collection.each(this.addArticle); }, newArticlesCoounter: 0, newArticle: function(){ this.newArticlesCoounter++; //creo un nuovo model per l'articolo var newModel = new ArticleModel({ title: "Nuovo articolo "+this.newArticlesCoounter, article: "BOOM! Corpo nuovo articolo.." }); //aggiungo il model alla collection, in modo da innescare la funzione 'addArticle', //come stabilito all'interno della funzione 'initialize' di questa view this.collection.add(newModel); } }); |
View di dettaglio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
//view per il dettaglio dell'articolo var ArticleView = Backbone.View.extend({ // diciamo a backbone che deve inserire il markup del template di questa view all'interno di un oggetto dal tag 'section' tagName: "section", //settiamo la classe che deve avere il tag 'section' className: "article-wrapper", //impostaiamo l'id del template da cui generare il merkup di questa view template: "#article-template", //impostazione degli eventi per l'interazione dell'utente (il funzionamento ed i selettori sono come quelli di jQuery). events:{ //evento //selettore //funzione da eseguire "click [data-js=close]": "hide" }, //la funzione che viene eseguita al momento dell'inizializzazione di questa view initialize: function() { //compiliamo il template in una variabile this.template = _.template($(this.template).html()); //rimuoviamo il modal dal DOM quando esso viene nascosto this.$el.on("hidden.bs.modal", function(){ this.remove(); }); }, // Effettua il render del markup della view per il dettaglio dell'articolo, in base al modello con cui questa view è stata inizializzata. render: function() { $(this.el).html(this.template(this.model.toJSON())); return this; }, //funzione che mostra l'elemento DOM nell'interfaccia show: function(){ //aggiungendo la classe 'show-article' al contenitore di questa view //(elemento 'section.article-wrapper') inneschiamo l'animazione di entrata this.$el.addClass("show-article"); }, //funzione che nasconde l'elemento DOM dall'interfaccia hide: function(){ var _this = this; //aggiungendo la classe 'hide-article' al contenitore di questa view //(elemento 'section.article-wrapper') inneschiamo l'animazione di uscita _this.$el.addClass("hide-article"); //al termine della'animazione, rimuoviamo l'oggetto DOM dal documento tramite il metodo backbone 'remove()', in modo da alleggerire il lavoro //della memoria centrale del dispositivo, contribuendo a rendere la nostra applicazione più fluida e performante _this.$el.on( animEndEventName ,function(){ _this.remove(); }); } }); |
Il codice CSS
Per quanto riguarda gli stili CSS3, abbiamo utilizzato delle posizioni assolute per elementi come l’header, il contenitore della lista ed il tag <section> legato alla view del dettaglio. A quest’ultimo abbiamo inoltre applicato la tecnica dell’accelerazione hardware (o hardware acceleration) tramite l’istruzione ‘transform: translate3d(0,0,0)‘, corredata con i vari prefix per la compatibilità browser, che renderà la nostra HTML5 Mobile App più fluida e performante.
Così facendo, ci assicuriamo delle animazioni fluide e performanti, nel momento in cui all’elemento ‘section.article-wrapper‘ vengono aggiunte le classi che le ‘innescano': ‘.show-article‘ e ‘.hide-article‘.
Ecco il codice necessario per realizzare l’effetto di entrata:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
section.article-wrapper { /*stili per il posizionamento*/ position: fixed; left: 0; top: 0; right: 0; bottom:0; z-index: 30; visibility: hidden; /* migliora le prestazioni prevenendo il render della parte 'posteriore' dell'elemento */ -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden; /* forziamo l'utilizzo della tecnica dell'accelerazione hardware per avere aniamzioni più fluide e reattive; mpostando come elemento scatenante per l'effetto la proprietà 'translate3d' il processo di rendering per l'elemento sarà delegato alla GPU (memoria grafica) prima, dopo e durante l'animazione */ -webkit-transform: translate3d(0,0,0); -moz-transform: translate3d(0,0,0); -ms-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } section.article-wrapper.show-article{ /* nel momento in cui all'elemento 'section.article-wrapper' viene aggiunta la classe 'show-article' innescchiamo l'animazione di entrata specifcandola in corrispondenza dell'attributo 'animation' */ -webkit-animation: showFromBottom .25s ease both; -moz-animation: showFromBottom .25s ease both; animation: showFromBottom .25s ease both; visibility: visible; } section.article-wrapper.hide-article{ /* nel momento in cui all'elemento 'section.article-wrapper' viene aggiunta la classe 'hide-article' inneschiamo l'animazione di uscita specificandola in corrispondenza dell'attributo 'animation' */ -webkit-animation: hideToBottom .25s ease both; -moz-animation: hideToBottom .25s ease both; animation: hideToBottom .25s ease both; } /* definizione delle animazioni di entrata e di uscita per la view dell'articolo: muoviamo sempliemente l'elemento in verticale sull'asse delle ordinate */ /*animazione di entrata*/ @-webkit-keyframes showFromBottom { from { -webkit-transform: translateY(100%); } } @-moz-keyframes showFromBottom { from { -moz-transform: translateY(100%); } } @keyframes showFromBottom { from { transform: translateY(100%); } } /*animazione di uscita*/ @-webkit-keyframes hideToBottom { to { -webkit-transform: translateY(100%); } } @-moz-keyframes hideToBottom { to { -moz-transform: translateY(100%); } } @keyframes hideToBottom { to { transform: translateY(100%); } } |
Ultimo step: avviamo la nostra HTML5 Mobile App!
Ora che abbiamo tutti i pezzi del puzzle possiamo procedere ed avviare la nostra applicazione: per instanziare i nostri componenti ed avviare l’applicazione, torniamo sul file ‘demo.js‘ e ci serviremo del seguente codice javascript per inserire nella collection qualche articolo predefinito, con tanto di titolo, immagine, corpo dell’articolo e data, in modo da visualizzarli subito nella lista iniziale:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
$(function(){ //creiamo una nuova istanza per la collection degli articoli con 4 models iniziali var articles = new ArticleList([ { title: "App su upCreative.net", image: "1.jpg", article: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }, { title: "Secondo articolo su upCreative.net", image: "2.jpg", article: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur" }, { title: "Articolo senza immagine 3", article: "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat" }, { title: "Quarto articolo sulla lista", image: "3.jpg", article: "Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat" } ]); //creiamo una nuova istanza per la view dell'applicazione //principale associandogli la collection var app = new AppView({ collection : articles }); }); |
BOOM! L’esperimento è pronto, di seguito puoi consultare la demo o poterti divertire direttamente con i sorgenti.
Conclusioni, demo e download
Tramite questa demo è possible intuire, almeno in parte, le potenzialità che backbone ed il pattern MVC mettono a disposizione. Come avrai potuto notare ogni view agisce in modo “non intrusivo” nei confronti degli altri elementi: ognuna di esse “resta in ascolto” ad eventuali modifiche apportate al model o alla collection associati, attraverso l’istruzione ‘this.listenTo‘.
Quando ciò accade, è possibile effettuare le relative modifiche al DOM della view senza doverle richiamare da parti esterne dell’applicazione ma agendo del tutto autonomamente. Di conseguenza l’applicazione stessa risulta maggiormente scalabile ed estendibile secondo le proprie esigenze.
E tu cosa ne pensi? Hai mai utilizzato Backbone per i tuoi progetti? Fammelo sapere lasciando un commento!
Crea la tua mobile app in HTML, CSS3 e Javascript
Utilizzando questa tecnica insieme a molte altre, ho realizzato più di 10 mobile app in HTML, CSS3 e Javascript (tra cui Tint e Dieta SI o NO?).
Se anche tu vuoi realizzare la tua applicazione in HTML, CSS e javascript da distribuire nei vari store, forse potrebbe interessarti il mio e-book HTML Mobile Accelerato.
Giacomo Freddi
Web Designer Freelance e Developer, si occupa del design e dello sviluppo di applicazioni web dal 2008, come molti freelance è abituato a gestire più ruoli e spaziare su più campi, ma la sua passione principale è quella della creazione di interfacce front-end e back-end utilizzando codice html5 e css3. Adora usare pattern MVC per i suoi Javascript.
Pingback: Smplice HTML5 Mobile App con Bootstrap Ratchet e Backbone.js | upCreative()
Pingback: Come creare un app: realizzare una view di login con il localStorage | upCreative()