{"id":12166,"date":"2016-12-14T12:12:33","date_gmt":"2016-12-14T11:12:33","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=12166"},"modified":"2016-12-14T12:13:27","modified_gmt":"2016-12-14T11:13:27","slug":"knockout-js-vediamolo-al-lavoro-con-un-esempio-pratico","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/knockout-js-vediamolo-al-lavoro-con-un-esempio-pratico\/","title":{"rendered":"Knockout.js: vediamolo al lavoro con un esempio pratico"},"content":{"rendered":"<p>In un <a href=\"http:\/\/www.devapp.it\/wordpress\/introduzione-a-knockout-js\/\">articolo precedente<\/a>, abbiamo introdotto il framework Knockout.js e le sue peculiarit\u00e0 principali, tra cui:<\/p>\n<ul>\n<li>l&#8217;architettura basata sul patten Model-View-ViewModel (MVVM) che permette di separare la logica di presentazione dei dati, il\u00a0layout e la logica di business dell&#8217;applicazione;<\/li>\n<li>il <em>databinding<\/em>\u00a0tramite il quale si pu\u00f2 aggiornare automaticamente l&#8217;interfaccia utente \u00a0in base ai dati contenuti nel Model e viceversa;<\/li>\n<li>gli <em>observables<\/em> che attuano\u00a0il databinding in pratica.<\/li>\n<\/ul>\n<p>In questo post, vedremo il framework al lavoro in un esempio pi\u00f9 completo dove il\u00a0layout sar\u00e0 impostato\u00a0con il\u00a0 framework <a href=\"http:\/\/getbootstrap.com\/\" target=\"_blank\">Bootstrap<\/a>: il codice pu\u00f2 essere scaricato in <a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/06\/esempio-approfondimento-KnockoutJS.zip\">formato zip<\/a>.<\/p>\n<p>L&#8217;interfaccia apparir\u00e0 come nell&#8217;immagine seguente:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-12178\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/06\/knockoutjs-bootstrap-javascript_img_01.jpg\" alt=\"knockoutjs-bootstrap-javascript_img_01\" width=\"1137\" height=\"495\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/06\/knockoutjs-bootstrap-javascript_img_01.jpg 1137w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/06\/knockoutjs-bootstrap-javascript_img_01-300x131.jpg 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/06\/knockoutjs-bootstrap-javascript_img_01-768x334.jpg 768w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2016\/06\/knockoutjs-bootstrap-javascript_img_01-1024x446.jpg 1024w\" sizes=\"auto, (max-width: 1137px) 100vw, 1137px\" \/><\/p>\n<p>e sar\u00e0 costituita da tre pannelli:<\/p>\n<ul>\n<li>&#8220;Inserimento&#8221;: contenente un form per l&#8217;introduzione di nuovi\u00a0dati relativi ad\u00a0una persona. Per ogni soggetto, saranno registrati un nome, un cognome, l&#8217;et\u00e0, la disponibilit\u00e0 di un&#8217;automobile\u00a0e lo stato civile;<\/li>\n<li>&#8220;Elenco&#8221;: un elenco dinamico in cui verranno mostrati gli oggetti inseriti;<\/li>\n<li>&#8220;Statistiche&#8221;: una piccola griglia che riporta statistiche sugli inserimenti come il numero di persone registrate e l&#8217;et\u00e0 media.<\/li>\n<\/ul>\n<h2>L&#8217;interfaccia<\/h2>\n<p>L&#8217;interfaccia utente, come detto, \u00e8 stata progettata con Bootstrap, un framework CSS inventato da Twitter, uno degli strumenti pi\u00f9 usati nel web design. Questo quanto apparir\u00e0 nel file HTML:<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n &lt;head&gt;\r\n    &lt;meta charset=\"utf-8\"&gt;\r\n    &lt;meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"&gt;\r\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\r\n    &lt;title&gt;Gestione del personale&lt;\/title&gt;\r\n\r\n    &lt;link href=\"css\/bootstrap.min.css\" rel=\"stylesheet\"&gt;\r\n\t&lt;link href=\"css\/stile.css\" rel=\"stylesheet\"&gt;\r\n\r\n    &lt;!--[if lt IE 9]&gt;\r\n      &lt;script src=\"https:\/\/oss.maxcdn.com\/html5shiv\/3.7.2\/html5shiv.min.js\"&gt;&lt;\/script&gt;\r\n      &lt;script src=\"https:\/\/oss.maxcdn.com\/respond\/1.4.2\/respond.min.js\"&gt;&lt;\/script&gt;\r\n    &lt;![endif]--&gt;\r\n &lt;\/head&gt;\r\n &lt;body&gt;\r\n\t &lt;div class=\"container\"&gt;\r\n\t\t&lt;div class=\"col-md-8 blocco\"&gt;\r\n\t\t\t&lt;div class=\"intestazione\"&gt;\r\n\t\t\tInserimento\r\n\t\t\t&lt;\/div&gt;\r\n\t\t\t&lt;div class=\"corpo\"&gt;\r\n\t\t\t\t&lt;form&gt;\r\n\t\t\t\t  &lt;div class=\"form-group row\"&gt;\r\n\t\t\t\t\t&lt;label class=\"col-md-2\"&gt;Cognome&lt;\/label&gt;\r\n\t\t\t\t\t&lt;div class=\"col-md-9\"&gt;\r\n\t\t\t\t\t\t&lt;input type=\"text\" class=\"form-control\" data-bind=\"value: cognome\"&gt;\r\n\t\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t  &lt;\/div&gt;\r\n\t\t\t\t  &lt;div class=\"form-group row\"&gt;\r\n\t\t\t\t\t&lt;label class=\"col-md-2\"&gt;Nome&lt;\/label&gt;\r\n\t\t\t\t\t&lt;div class=\"col-md-9\"&gt;\r\n\t\t\t\t\t\t&lt;input type=\"text\" class=\"form-control\" data-bind=\"value: nome\"&gt;\r\n\t\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t  &lt;\/div&gt;\r\n\t\t\t\t  &lt;div class=\"form-group row\"&gt;\r\n\t\t\t\t\t&lt;label class=\"col-md-2\"&gt;Et\u00e0&lt;\/label&gt;\r\n\t\t\t\t\t&lt;div class=\"col-md-3\"&gt;\r\n\t\t\t\t\t\t&lt;input type=\"text\" class=\"form-control\" data-bind=\"value: eta\"&gt;\r\n\t\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t  &lt;\/div&gt;\r\n\t\t\t\t  &lt;div class=\"form-group row\"&gt;\r\n\t\t\t\t\t&lt;label  class=\"col-md-3\"&gt;&lt;input type=\"checkbox\" data-bind=\"checked: automunito\"&gt; Automunito&lt;\/label&gt;\r\n\t\t\t\t\t&lt;div class=\"col-md-9\"&gt;\r\n\t\t\t\t\t&lt;label&gt;Stato civile&lt;\/label&gt;\r\n\t\t\t\t\t&lt;select data-bind=\"value: statoCivileSelezionato, options: elencoStatoCivile, optionsCaption: '-- Scegliere uno Stato Civile --'\"&gt;&lt;\/select&gt;\r\n\t\t\t\t\t &lt;\/div&gt;\r\n\t\t\t\t  &lt;\/div&gt;\r\n\t\t\t\t  &lt;div class=\"form-group row\"&gt;\r\n\t\t\t\t    &lt;div class=\"text-center buttons\"&gt;\r\n\t\t\t\t\t\t&lt;button type=\"submit\" class=\"btn btn-default\" data-bind=\"click: aggiungiPersona\"&gt;Salva&lt;\/button&gt;\r\n\t\t\t\t\t\t&lt;button type=\"submit\" class=\"btn btn-default\" data-bind=\"click: pulisciForm\"&gt;Pulisci&lt;\/button&gt;\r\n\t\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t  &lt;\/div&gt;\r\n\t\t\t\t&lt;\/form&gt;\r\n\t\t\t   &lt;\/div&gt;\r\n\t\t\t&lt;div class=\"intestazione\"&gt;\r\n\t\t\tElenco\r\n\t\t\t&lt;\/div&gt;\r\n\t\t\t&lt;div class=\"corpo\"&gt;\r\n\t\t\t\t&lt;div data-bind=\"foreach: elenco\" class=\"scrollable\" &gt;\r\n\t\t\t\t\t&lt;div class=\"list-group-item t1\"&gt;&lt;span data-bind=\"text: nome\"&gt;&lt;\/span&gt; &lt;span data-bind=\"text: cognome\"&gt;&lt;\/span&gt; di anni &lt;span data-bind=\"text: eta\"&gt;&lt;\/span&gt;&lt;\/div&gt;\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t&lt;\/div&gt;\r\n\t\t&lt;\/div&gt;\r\n\t\t&lt;div class=\"col-md-4 blocco\"&gt;\r\n\t\t\t&lt;div class=\"intestazione\"&gt;\r\n\t\t\tStatistiche\r\n\t\t\t&lt;\/div&gt;\r\n\t\t\t&lt;div&gt;\r\n\t\t\t\t&lt;div class=\"t1 col-md-6\"&gt;\r\n\t\t\t\t\tNumero inserimenti\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t&lt;div class=\"t2 col-md-6\"&gt;\r\n\t\t\t\t\t&lt;span data-bind=\"text: elenco().length\"&gt;0&lt;\/span&gt;\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t&lt;div class=\"t1 col-md-6\"&gt;\r\n\t\t\t\t\tEt\u00e0 media\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t&lt;div class=\"t2 col-md-6\"&gt;\r\n\t\t\t\t\t&lt;span data-bind=\"text: etaMedia\"&gt;0&lt;\/span&gt;\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t&lt;\/div&gt;\r\n\t\t&lt;\/div&gt;\r\n\t &lt;\/div&gt;\r\n &lt;script src=\"http:\/\/code.jquery.com\/jquery.js\"&gt;&lt;\/script&gt;\r\n &lt;script src=\"js\/bootstrap.min.js\"&gt;&lt;\/script&gt;\r\n &lt;script type='text\/javascript' src=\"js\/knockout-3.4.0.js\"&gt;&lt;\/script&gt;\r\n  &lt;script type='text\/javascript'&gt;\r\n    \/\/ codice relativo a Knockout.js\r\n  &lt;\/script&gt;\r\n &lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>Il codice dell&#8217;esempio \u00e8 qui mostrato\u00a0integralmente, manca solo la porzione di Javascript che verr\u00e0 affrontata nel paragrafo seguente.<\/p>\n<p>Per poterlo eseguire, \u00e8 necessario:<\/p>\n<ul>\n<li><a href=\"http:\/\/getbootstrap.com\/getting-started\/#download\" target=\"_blank\">scaricare Bootstrap<\/a> o metterlo a disposizione tramite\u00a0CDN;<\/li>\n<li>scaricare <a href=\"http:\/\/knockoutjs.com\/downloads\/\" target=\"_blank\">Knockout.js<\/a>;<\/li>\n<li>mettere a disposizione <a href=\"https:\/\/jquery.com\/download\/\" target=\"_blank\">jQuery <\/a>in qualche maniera.<\/li>\n<\/ul>\n<p>Tutte le librerie ed i framework necessari verranno introdotti con i necessari tag &lt;script&gt; e, quanto scaricato, sar\u00e0 organizzato in due cartelle che, nel nostro esempio, si chiamano <em>js<\/em> per il codice Javascript e <em>css<\/em> per i fogli di stile. A proposito di quest&#8217;ultimo aspetto, abbiamo creato un nostro foglio di stile &#8211; collegato al HTML precedente &#8211; denominato stile.css il cui contenuto \u00e8 il seguente:<\/p>\n<pre class=\"lang:css decode:true\">.blocco\r\n\t{\r\n\t\tpadding: 5px;\r\n\t}\r\n\t\r\n\t.intestazione\r\n\t{\r\n\t\tpadding: 5px;\r\n\t\tbackground-color: #cc5200;\r\n\t\tcolor: #FFFFFF;\r\n\t\tfont-weight: bold;\r\n\t\tfont-size:16px;\r\n\t\tborder-top-right-radius: 10px;\r\n\t\tborder-top-left-radius: 10px;\r\n\t}\r\n\t.corpo\r\n\t{\r\n\t\tbackground-color: #ffa366;\r\n\t\tpadding-top: 5px;\r\n\t\tpadding-right: 5px;\r\n\t\tpadding-left: 5px;\r\n\t\tmargin-bottom: 5px;\r\n\t}\r\n\t\r\n\t.row-bgc\r\n\t{\r\n\t\tbackground-color: #ffa366;\r\n\t}\r\n\t\r\n\t.buttons{\r\n\t\tpadding:8px\r\n\t}\t\r\n\r\n\t.scrollable {\r\n\t\theight: auto;\r\n\t\tmax-height: 200px;\r\n\t\toverflow-x: hidden;\r\n\t}\r\n\t\r\n\t.t1 {\r\n\t\tbackground-color: #FF6600;\r\n\t\tpadding: 10px;\r\n\t\tcolor: #FFFFFF;\r\n\t\tfont-weight: bold;\r\n\t}\r\n\t.t2 {\r\n\t\tbackground-color: #FF8533;\r\n\t\tpadding: 10px;\r\n\t\tcolor: #FFFFFF;\r\n\t\tfont-weight: bold;\r\n\t}<\/pre>\n<h2>Gestione dei dati con Knockout.js<\/h2>\n<p>Il codice Javascript essenzialmente sar\u00e0 diviso in tre porzioni:<\/p>\n<ul>\n<li>creeremo una funzione che\u00a0servir\u00e0 per definire gli oggetti di tipo Persona, ognuno dei quali ospiter\u00e0 i dati inseriti tramite l&#8217;utilizzo del form;<\/li>\n<\/ul>\n<ul>\n<li>un&#8217;altra funzione fornir\u00e0 il ViewModel che utilizzeremo;<\/li>\n<\/ul>\n<ul>\n<li>infine, tramite il metodo\u00a0dell&#8217;oggetto <em>ko<\/em> agganceremo il nostro ViewModel all&#8217;interfaccia utente.<\/li>\n<\/ul>\n<p>Schematicamente sar\u00e0 questa la struttura, successivamente approfondiremo il ViewModel nel dettaglio:<\/p>\n<pre class=\"lang:js decode:true\">function Persona(nome, cognome, eta, automunito, statocivile) {\r\n\t\tvar self = this;\r\n\t\tself.nome = nome;\r\n\t\tself.cognome = cognome;\r\n\t\tself.eta = eta;\r\n\t\tself.automunito = automunito;\r\n\t\tself.statocivile = statocivile;\r\n\t}\r\n\t\r\n\tfunction GestionePersonale() {\r\n\t\t\/*\r\n                 * ViewModel: lo approfondiremo a breve\r\n                 *\/\r\n\t}\r\n\t\r\n\tko.applyBindings(new GestionePersonale());<\/pre>\n<p>Nel ViewModel dovremo innanzitutto definire alcune propriet\u00e0 che saranno rappresentate da <em>observable<\/em>, quel tipo di oggetti &#8211; di cui abbiamo parlato nel precedente articolo &#8211; il cui valore viene costantemente sincronizzato con specifici controlli utente dell&#8217;interfaccia.<\/p>\n<pre class=\"lang:js decode:true \">function GestionePersonale() {\r\n\t\tvar self = this;\r\n\t\t\r\n\t\tself.elenco = ko.observableArray([]);\r\n\t\t\r\n\t\tself.cognome=ko.observable();\r\n\t\tself.nome=ko.observable();\r\n\t\tself.eta=ko.observable();\r\n\t\tself.automunito=ko.observable();\r\n\t\tself.elencoStatoCivile=ko.observableArray(['coniugato', 'celibe\/nubile', 'separato\/divorziato']);\r\n\t\tself.statoCivileSelezionato=ko.observable();\r\n\t\tself.totaleEta=0;\r\n\t\tself.etaMedia=ko.observable(0);\r\n\t\tself.quantisono=ko.observable(0);\r\n\t\r\n\t\t\r\n\t\tself.aggiungiPersona = function() {\r\n\t\t\tself.elenco.push(new Persona(self.nome(),self.cognome(), self.eta(),self.automunito(), self.statoCivileSelezionato()));\r\n\t\t\tself.totaleEta+=parseInt(self.eta());\r\n\t\t\tself.etaMedia(self.totaleEta\/self.elenco().length);\r\n\t\t\tself.pulisciForm();\r\n\t\t}\r\n\t\t\r\n\t\tself.pulisciForm=function()\r\n\t\t{\r\n\t\t\tself.nome(\"\");\r\n\t\t\tself.cognome(\"\");\r\n\t\t\tself.automunito(false);\r\n\t\t\tself.eta(\"\");\r\n\t\t\tself.statoCivileId=0;\r\n\t\t}\r\n\t}<\/pre>\n<p>Si noti, tra l&#8217;altro, che ci sono alcuni observable definiti tramite il metodo\u00a0<em>observableArray<\/em> che crea un array di oggetti observable. Ci\u00f2 \u00e8 utile quando si ha una lista di elementi che deve in qualche maniera essere riprodotta nell&#8217;interfaccia.<\/p>\n<p>Qui usiamo due observableArray: uno per contenere i vari oggetti Persona creati che saranno poi visualizzati iterativamente in un elenco, l&#8217;altro per alimentare il menu a tendina che mostra gli stati civili selezionabili. Il primo di questi due sar\u00e0 direttamente trasformato in un elenco HTML tramite il seguente binding di tipo <em>foreach<\/em>:<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;div class=\"corpo\"&gt;\r\n &lt;div data-bind=\"foreach: elenco\" class=\"scrollable\" &gt;\r\n  &lt;div class=\"list-group-item t1\"&gt;&lt;span data-bind=\"text: nome\"&gt;&lt;\/span&gt; \r\n      &lt;span data-bind=\"text: cognome\"&gt;&lt;\/span&gt; di anni &lt;span data-bind=\"text: eta\"&gt;\r\n      &lt;\/span&gt;\r\n  &lt;\/div&gt;\r\n &lt;\/div&gt;\r\n&lt;\/div&gt;<\/pre>\n<p>Un binding di questo tipo ha l&#8217;effetto di ripetere tutto il testo HTML, inserito all&#8217;interno del tag di cui fa parte,\u00a0tante volte quante sono gli oggetti\u00a0presenti nell&#8217;observableArray attivando al suo interno gli eventuali binding presenti. I due\u00a0ulteriori metodi presenti nel ViewModel svolgeranno compiti piuttosto prevedibili:<\/p>\n<ul>\n<li><em>aggiungiPersona<\/em> crea un nuovo oggetto Persona con i dati del form e lo inserisce nell&#8217;array di oggetti &#8220;osservabili&#8221; e, successivamente, aggiorna i dati per le statistiche. La sua attivazione \u00e8 indotta da un <em>binding<\/em> di tipo <em>click<\/em>, collegato al pulsante con etichetta &#8220;Salva&#8221;;\n<pre class=\"lang:xhtml decode:true \">&lt;button type=\"submit\" class=\"btn btn-default\" data-bind=\"click: aggiungiPersona\"&gt;Salva&lt;\/button&gt;<\/pre>\n<\/li>\n<li><em>pulisciForm<\/em> pulisce i campi del form dopo un avvenuto inserimento.\u00a0Esso, oltre che dall&#8217;interno del metodo <em>aggiungiPersona<\/em> pu\u00f2 essere invocato tramite binding di tipo click con il pulsante &#8220;Pulisci&#8221;.<\/li>\n<\/ul>\n<h2>Conclusioni<\/h2>\n<p>Sebbene l&#8217;esempio non sia dei pi\u00f9 complessi mostra in particolare che:<\/p>\n<ul>\n<li>Knockout.js pu\u00f2 entrare a far parte dei vostri progetti integrandosi con tutto ci\u00f2 che solitamente utilizzate: si veda, ad esempio, la presenza di Bootstrap per l&#8217;interfaccia;<\/li>\n<li>la struttura del codice \u00e8 piuttosto snella perch\u00e8 molte parti scontate vengono sempre assolte dal framework.<\/li>\n<\/ul>\n<p>Knockout.js \u00e8 un tipico figlio dell&#8217;ultima generazione dei prodotti Javascript: utile, rapido, comodo \u00a0e velocissimo da attivare.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In un articolo precedente, abbiamo introdotto il framework Knockout.js e le sue peculiarit\u00e0 principali, tra cui: l&#8217;architettura&#8230;<\/p>\n","protected":false},"author":561,"featured_media":12589,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[1538,1699,1598],"class_list":["post-12166","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorial-pratici","tag-javascript","tag-knockout-js","tag-web-developers"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12166","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/561"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=12166"}],"version-history":[{"count":18,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12166\/revisions"}],"predecessor-version":[{"id":12629,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12166\/revisions\/12629"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media\/12589"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=12166"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=12166"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=12166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}