Creare un’applicazione Aurelia con la dotnet CLI e i JavaScriptServices

Buone notizie per chi come me ha scelto Aurelia come framework di riferimento per scrivere Single Page Application. Un po' alla volta la creatura di Rob Eisenberg si sta ritagliando il suo spazio ed è ora un firstclass citizen nell'ultima versione della dotnet CLI, quella disponibile nella SDK 1.0 rc4 build 004771.

Per i pochi che si trovassero spaesati, stiamo parlando del tool a riga di comando (CommandLine Interface) che consente di eseguire tutte le operazioni legate alla generazione, esecuzione e pubblicazione di un applicativo basato su .NET Core.

È da qualche giorno disponibile una serie di pacchetti NuGet che estende i template di default della CLI consentendo di generare SPA basate sui principali framework presenti sul mercato. Accanto ad Angular e React fa bella mostra di sé anche il template per generare applicazioni basate su Aurelia. Vediamo cosa dobbiamo fare per utilizzarlo.

Come prima cosa dobbiamo assicurarci di avere installata la versione di SDK corrispondente alla build 004771 o superiore che potete eventualmente scaricare dal link precedente:

Dopo averlo installato, aprendo un prompt dei comandi e digitando dotnet --version dovreste ricevere l'indicazione 1.0.0-rc4-004771. A questo punto possiamo digitare dotnet new --help per verificare quali siano i template disponibili per la creazione di un nuovo progetto. Se avete appena installato la CLI potreste dover attendere un po'per il popolamento della cache locale:

Di base dovreste avere disponibili di seguenti template:

Per cui potremmo ad esempio digitare dotnet new web per creare una nuova applicazione ASP.NET Core vuota da usare come base per i nostri sviluppi.

Per installare i template dedicati alle applicazioni SPA dobbiamo invece digitare dotnet new --install Microsoft.AspNetCore.SpaTemplates::*:

Dopo aver installato un po' di package assortiti (nel mio caso 111) i nuovi template dovrebbero essere a vostra disposizione:

Proviamo subito a vedere "l'effetto che fa". Spostiamoci nella cartella dove vogliamo creare il progetto e digitiamo prima dotnet new aurelia e quindi dotnet restore. Il primo comando crea il progetto a partire dal template, il secondo effettua il restore dei due pacchetti NuGet utilizzati (NodeServices e SpaServices):

Il template contiene al suo interno il file di configurazione project.json che elenca tutti i pacchetti NPM necessari. Per ripristinarli è sufficiente digitare npm install dallo stesso prompt dei comandi. Dopo un po' di attesa abbiamo finalmente il nostro progetto pronto e possiamo lanciarlo con il comando dotnet run. Per default il progetto creato si mette in ascolto sulla porta 5000 ed è quindi sufficiente digitare http://localhost:5000 in un qualsiasi browser per navigarlo:

Ci sono alcune differenze da notare rispetto al progetto che sto portando avanti nella mia serie su Aurelia:

  • Il progetto generato dal template è per Visual Studio 2017. Come conseguenza il file di progetto xproj è stato sostituito dal file csproj e il file project.json è sparito. Per modificare il progetto potete quindi usare Visual Studio 2017, Visual Studio Code o ovviamente il vostro editor preferito. Il progetto è inoltre basato su ASP.NET Core 1.1 (invece che sull'1.0 come nella mia serie), ma questo è un aspetto che non al momento ha impatti e che affronteremo a breve anche noi;
  • Come si vede nello screenshot, il progetto così lanciato gira in ambiente di produzione, a differenza di quanto succede lanciando il progetto da dentro Visual Studio. Questo è dovuto al fatto che Visual Studio definisce per noi la variabile ASPNETCORE_ENVIRONMENT e la imposta al valore Development. Per ottenere lo stesso comportamento a riga di comando dobbiamo definire manualmente la stessa variabile, andando nell'apposita sezione del pannello di controllo o utilizzando il comando setx ASPNETCORE_ENVIRONMENT "Development" (in questo caso è necessario aprire un nuovo prompt dei comandi per "vedere" la nuova variabile);
  • Come avevamo avuto modo di dire, Aurelia supporta diversi package manager. Nella nostra serie stiamo usando JSPM mentre il progetto generato dal template per uniformità tra i vari framework utilizza invece Webpack. Il progetto include anche i concetti di bundling e minification che ancora non abbiamo affrontato nella serie, e potrebbe quindi essere un po' spiazzante il fatto di non trovare nella cartella wwwroot nessuno dei ViewModel e delle View che ci aspetteremmo di trovare (a dire il vero i file html mancano del tutto). Non è il momento di spiegare nel dettaglio il funzionamento di Webpack, in ogni caso se volete modificare il progetto i file che vi interessano si trovano nella cartella ClientApp;
  • Il progetto usa TypeScript invece che ES2015 per definire i ViewModel. Come vedrete se vi addentrerete tra i file, le differenze sono minime e si concretizzano soprattutto nella possibilità di tipizzare variabili e parametri. Se guardate in wwwroot vi accorgete che gli unici due file JavaScript (oltre a essere apparentemente incomprensibili) sono invece caratterizzati da una sintassi ES5. Questo è il risultato dell'uso del bundling di Webpack per ottimizzare prestazioni e compatibilità. Per capire meglio di cosa parlo provate a confrontare il tab Network dei Developer Tools (cioè l'F12) del vostro browser preferito: la differenza tra il numero di file caricati dal progetto della nostra serie e quelli di questo progetto vale più di mille parole (ma non disperate, ci arriveremo a suo tempo);
  • Il bundling di Webpack viene gestito attraverso un middleware ASP.NET Core dedicato che si trova nel package NuGet SpaServices referenziato nel progetto. Tale middleware è caricato nella pipeline di ASP.NET Core solo in ambiente di sviluppo ed è per questo che è fondamentale impostare la variabile ASPNETCORE_ENVIRONMENT come prima descritto. In caso contrario le vostre modifiche ai file presenti in ClientApp non sarebbero prese in considerazione da Webpack e i file in wwwroot rimarrebbero quelli originali
    ;
  • In teoria i JavaScriptServices supportano l'Hot Module Replacement ma in pratica, come si vede nello screenshot qui sopra, questa opzione è disabilitata nel template di Aurelia per via di un problema con il plugin che consente di fare funzionare il framework con Webpack. Intanto che il problema viene risolto cerchiamo di capire cosa ci stiamo perdendo: abbiamo detto in precedenza che, quando facciamo una modifica ai nostri file di progetto (html o js/ts), Webpack si occupa di aggiornare i file presenti in wwwroot che saranno poi quelli serviti al client. In un'applicazione SPA questo non è sufficiente per "vedere" la modifica nel browser. La coppia View/ViewModel è stata infatti già richiesta al server e da quel momento in poi il framework SPA la riutilizza per ridurre la necessità di effettuare richiesta al server. Per ottenere la vista modificata dobbiamo aggiornare l'intera pagina (in genere con F5, presupponendo di avere la cache del browser disabilitata) ma questo vuol dire ovviamente perdere completamente il contesto di esecuzione del nostro test, ritrovandosi con una nuova istanza applicativa. Ecco l'HMR vuole risolvere appunto questo problema, iniettando html e js aggiornati nella SPA senza perdere il contesto di esecuzione corrente.

Happy coding!

End-to-End Javascript Testing a DevOps@Work 2017

Sono ora disponibili slide (su docs.com) e demo (su GitHub) della sessione End-to-End Javascript Testing che ho presentato all'evento DevOps@Work 2017 di inizio febbraio.

Nel README, associato al repository sulla branch master, ci sono le istruzioni da seguire per far funzionare la demo. Potete navigare tra le tre branch per trovare il codice iniziale e quello delle due demo (la prima senza usare il pattern Page Object, la seconda usandolo).

Per qualsiasi problema non esitate a contattarmi via email o Twitter.

Colgo l'occasione per ringraziare tutti i partecipanti per l'attenzione e l'interesse che mi hanno dimostrato pur essendo a fine giornata. Come promesso ai tanti cui - vista l'ora tarda - non ho potuto dare risposta dopo la sessione, nelle prossime settimane affronterò qui sul blog alcuni dei temi che non abbiamo avuto modo di trattare nel corso della sessione.

Happy coding!

Aurelia e ASP.NET Core: aggiornare la versione di Aurelia usando JSPM

Questo post fa parte di una serie dedicata ad Aurelia e ASP.NET Core.

Prima di proseguire nel nostro cammino è opportuno aggiornare la versione di Aurelia che stiamo utilizzando per tener conto delle nuove feature e delle fix rilasciate nel frattempo dal team di sviluppo. È possibile che la vostra installazione di Aurelia sia già aggiornata all'ultima versione, a meno che non abbiate cominciato il viaggio in contemporanea con il mio primo post. In ogni caso questa procedura vi potrà essere utile in futuro.

Ci sono due cose che possiamo aggiornare: JSPM e i pacchetti Aurelia che abbiamo aggiunto al nostro progetto (aurelia-framework e aurelia-bootstrapper).

Come sappiamo abbiamo installato JSPM tramite NPM aggiungendo direttamente la referenza dentro il file package.json. Per aggiornarlo abbiamo ora diverse opzioni.

La cosa più semplice è tornare a sfruttare la buona integrazione di NPM in Visual Studio. Basta aprire il nodo Dependencies del nostro progetto e nella cartella npm cliccare con il tasto destro sul pacchetto jspm (che nel mio caso si trova alla versione 0.16.48). Dal menu contestuale possiamo quindi scegliere Update Package per avviare l'operazione di (eventuale) aggiornamento:

In realtà, pur essendo ad oggi disponibile la versione 0.16.52, se proviamo a lanciare l'aggiornamento non accade nulla e il progetto rimane alla versione 0.16.48. Ciò dipende dal modo in cui ho configurato JSPM all'interno del file package.json.

Per capire meglio, passiamo momentaneamente alla seconda opzione cui accennavo in precedenza… la famigerata "riga di comando". Come tutti i tool nati fuori dal mondo Microsoft, NPM dispone ovviamente di un'interfaccia a riga di comando che è la principale modalità di utilizzo dello strumento (un po' come per .NET Core che dispone di una sua CLI, dotnet, che viene poi wrappata da Visual Studio per svolgere le medesime funzionalità con una UI grafica).

Quando lo installiamo, Visual Studio provvede ad installare una sua istanza di Node (comprensiva ovviamente di NPM) nella cartella C:\Program Files (x86)\Microsoft Visual Studio 14.0\Web\External (almeno per Visual Studio 2015). Si tratta dell'istanza che viene utilizzata dalla IDE per svolgere in background le attività scatenate ad esempio dall'Update Package di cui abbiamo parlato sopra. Se infatti guardiamo nella sezione Bower/npm della finestra di Output possiamo vedere che l'operazione che abbiamo appena eseguito è stata trasformata in una chiamata al comando npm update jspm:

Per quanto sia possibile utilizzare quella stessa istanza per i nostri scopi, addentrandosi nello sviluppo web basato su Aurelia (o su altri framework simili) è bene installare Node (e quindi NPM) direttamente sulla propria macchina. Per farlo andiamo sul sito di Node e scarichiamo l'ultima LTS disponibile per il nostro sistema operativo (nel mio caso la 6.9.5 per Windows x64).

Dopo aver scaricato il file di setup, lanciamolo e seguiamo le semplici istruzioni fino ad arrivare al termine del processo. L'installazione provvede a registrare la cartella di installazione tra le PATH di sistema rendendo in questo modo possibile utilizzare NPM da qualunque prompt dei comandi:

Sfruttiamo subito questa possibilità andando ad installare JSPM in maniera "globale" sulla nostra macchina usando il comando npm install -g jspm e apriamo quindi un altro prompt dei comandi nella cartella del progetto, utilizzando ad esempio la comoda voce disponibile nel menu contestuale dei Productivity Power Tools:

Dal prompt digitiamo npm outdated:

Questo comando consente di verificare quali siano i pacchetti obsoleti presenti nella nostra applicazione. Nel nostro caso ci indica che il pacchetto jspm si trova attualmente alla versione 0.16.48 (Current) mentre l'ultima disponibile è la 0.16.52 (Latest). Ci dice però che il pacchetto che noi vogliamo è proprio il 0.16.48 (Wanted) e per questo motivo il comando di update non lo aggiorna anche se lo lanciamo da riga di comando:

Come dicevo, tale comportamento è dovuto al modo in cui ho referenziato JSPM al momento della sua installazione. Ci sono infatti diversi modi per configurare gli aggiornamenti dei pacchetti in NPM e i principali e più utilizzati sono tre:

  • Una versione specifica: indicando uno specifico numero di versione (come ho fatto io scrivendo "jspm" : "0.16.48") istruiremo NPM ad utilizzare sempre e soltanto quella particolare versione (per questo motivo gli aggiornamenti che abbiamo tentato non sono andati a buon fine);
  • Una versione con tutte le sue patch: indicando un numero di versione preceduto dalla tilde (~) istruiremo invece NPM ad utilizzare quella versione o una qualsiasi delle sue successive patch, ad esempio la 0.16.52 e più in generale la 0.16.y con y >= 48;
  • Una versione con tutte le sue minor release: indicando un numero di versione preceduto dall'accento circonflesso (^) istruiremo infine NPM ad utilizzare quella versione o una qualsiasi delle sue successive minor release, ad esempio la 0.17.4 e più in generale la 0.x.y con x >= 16 e y qualsiasi.

NPM utilizza il semantic versioning per assegnare i numeri di versione ai pacchetti, quindi sia utilizzando la tilde che l'accento circonflesso ci garantiamo che nessun aggiornamento potrà portare delle "breaking changes" che possano determinare un disservizio della nostra applicazione (avremo modo di tornare su questo aspetto a breve).

Come dicevo queste tre opzioni sono così comuni che sono pienamente supportante dall'Intellisense in fase di modifica manuale del file package.json:

Modifichiamo quindi l'impostazione usando l'impostazione minor release ed eseguiamo nuovamente l'aggiornamento di JSPM.

Questa volta dovremo ritrovarci con la versione 0.16.52 installata.

Aggiornato JSPM è il momento di aggiornare Aurelia. In questo caso Visual Studio non ci supporta e dobbiamo quindi necessariamente andare a riga di comando. Apriamo nuovamente un prompt nella cartella del progetto e digitiamo jspm update:

JSPM ci notifica gli aggiornamenti che vengono fatti. Come possiamo vedere anche nel file config.json, dei due pacchetti solo aurelia-framework è stato aggiornato (dalla 1.0.7 alla 1.0.8) determinando un corrispettivo aggiornamento dei pacchetti ad esso collegati:

Anche in questo caso il comportamento di JSPM è dipeso dalle impostazioni che abbiamo indicato al momento dell'installazione (o per meglio dire in questo caso, che il tool Package Installer ha indicato per noi). In questo caso avevamo usato l'accento circonflesso, che ha determinato l'aggiornamento alla versione 1.0.8 di aurelia-framework e il mantenimento della versione 1.0.1 dell'aurelia-bootstrapper:

In effetti, se cerchiamo su NPM il pacchetto aurelia-bootstrapper, possiamo verificare che l'ultima versione disponibile è la 2.0.1 che ha quindi un numero di versione major differente:

Se cerchiamo direttamente su GitHub, possiamo verificare che l'ultima versione disponibile avente versione major 1 è proprio la 1.0.1 e per tale motivo JSPM non ha aggiornato questo pacchetto (che come detto in precedenza potrebbe avere delle breaking changes) come scelta conservativa per evitare malfunzionamenti alla nostra applicazione:

Per forzare l'aggiornamento del bootstrapper (cosa che non vi consiglio di fare, almeno fino al prossimo post) dobbiamo modificare il file package.json, ad esempio come segue:

Se eseguiamo nuovamente un jspm update avremo finalmente la nuova versione del bootstrapper installata nel progetto:

Se lanciamo nuovamente l'applicazione per verificare che tutto sia andato come previsto, ci accorgiamo purtroppo che la possibile breaking change di aurelia-bootstrapper ha avuto uno spiacevole impatto sul caricamento di uno dei moduli di Aurelia, l'aurelia-pal-browser. Come risultato l'applicazione non parte più e ci è chiarissimo il perché l'impostazione di base per regolare gli aggiornamenti sia quella con l'accento circonflesso che evita il passaggio automatico a major version successive:

Nel prossimo post cercheremo di capire cosa è successo e come rimediare all'inconveniente.

Happy coding!

Un po’ di ALM non fa mai male

Nelle prossime settimane sarà impegnato in due eventi su ALM e DevOps organizzati da DomusDotNet e GetLatestVersion.it.

Il 31 gennaio dalle 18.00 alle 19.00 sarò on-line su Live Meeting insieme agli altri amici di GetLatestVersion.it per Ask Me Anything, una sessione aperta di domande e risposte sulle tematiche legate all'ALM e a DevOps. Se avete qualche dubbio in materia, che sia teorico o pratico, è l'occasione ideale per fugarlo.

Il 10 febbraio sarò invece presso la sede Microsoft di Roma per DevOps@Work 2017, l'ormai classico appuntamento di inizio anno organizzato da DomusDotNet con la partecipazione di GetLatestVersion.it. Anche quest'anno ci saranno due track e tante sessioni tra cui poter scegliere con speaker di qualità eccezionale.

Per l'occasione introdurrò i più mattinieri a Visual Studio Team Services, cercando di realizzare l'ardua impresa di passare in un'ora dal requisito di business al codice funzionante su Azure attraverso pianificazione dell'attività, organizzazione del codice sorgente, verifica della qualità per mezzo di test automatizzati e gestione delle build e delle release.

Per gli stakanovisti dell'evento, a fine giornata parlerò invece di testing in ambiente JavaScript, esaminando le tecniche ed i framework che ci consentono di garantire la qualità anche delle nostre applicazioni web con una forte componente client, come le Single Page Application.

Non esitate a registrarvi ai link degli eventi. Vi aspetto on-line e di persona.

Happy coding!

Aurelia e ASP.NET Core: binding di base in Aurelia

Questo post fa parte di una serie dedicata ad Aurelia e ASP.NET Core.

Al termine del post precedente ci eravamo lasciati con la necessità di idratare la nostra View con i dati provenienti dal ViewModel utilizzando un meccanismo detto binding. Vediamo quindi quali strumenti Aurelia ci mette a disposizione per gestire questa operazione.

Attribute binding, event binding e string interpolation

Riprendiamo quindi il nostro template e vediamo in che modo dobbiamo modificarlo per aggiungere la sintassi relativa al binding caratteristica di Aurelia:

Cominciamo da title (1) e master (2) per i quali utilizzeremo la string interpolation. Come suggerisce il nome, si tratta di una sintassi particolarmente utile nelle situazioni in cui si voglia usare testo dinamico proveniente dal ViewModel eventualmente insieme a testo statico inserito direttamente nella View. Non sorprenderà che Aurelia gestisca questo scenario facendo riferimento agli standard ES2015 per la string interpolation, cioè con la sintassi ${expression} in cui expression è una qualsiasi espressione JavaScript. Ad esempio <div>static text ${game.description} more static text </div> per inserire il valore della proprietà description in un div intercalandolo con del testo statico. Nel nostro caso avremo quindi:

Passiamo al button per unirsi ad una partita (3) e al link per partecipare ad una sessione di gioco (4). In questo caso abbiamo due problemi da risolvere: dobbiamo rendere visibili i due elementi a seconda del valore delle proprietà canJoin e canPlay, e dobbiamo eseguire il metodo join in seguito al click sul button. Per risolverli useremo le altre due sintassi previste da Aurelia, attribute ed event binding, più un'altra particolarità estremamente flessibile e potente di Aurelia: i custom attribute.

L'attribute binding è caratterizzato dalla sintassi attribute.command="expression". Dato un qualunque attributo (attribute), è possibile valorizzarlo con in risultato di un'espressione JavaScript (expression) attraverso l'applicazione di uno tra i comandi supportati (command). Approfondiremo il concetto dei comandi (che servono per definire direzione e frequenza degli aggiornamenti) in un prossimo post, per ora accontentiamoci di usare il comando generico bind che determina in maniera intelligente il tipo di comando specifico da applicare (con una modalità detta binding adattativo che è caratteristica di Aurelia). Potremmo ad esempio scrivere <a href.bind="game.url">Vai alla partita</a>. per utilizzare il contenuto della proprietà url come destinazione del link Vai alla partita.

Nel nostro caso abbiamo però un altro problema. In HTML5 non esiste (ovviamente) un attributo specifico che specifichi se vogliamo includere o meno un certo elemento nel DOM della pagina. Essendo una questione centrale in un'applicazione SPA, ogni framework che si rispetti ha dovuto affrontarla e risolverla. Invece di inventare una sintassi proprietaria (e magari astrusa) come per altri framework, Aurelia cerca come al solito di rimanere il più possibile aderente alle specifiche HTML5 e mette a disposizione degli attributi custom con cui poter decorare gli elementi del DOM per ottenere comportamenti personalizzati. Non solo Aurelia è fornito di alcuni custom attribute out-of-the-box, ma ci mette a disposizione l'architettura per poterne definire a nostra volta con una semplicità disarmante. Per oggi però non affronteremo questo tema ma ci limiteremo ad usare due di questi attributi builtin. Il primo è l'if è serve appunto a discriminare la presenza o meno di un elemento nel DOM a seconda dell'espressione utilizzata nel bind. In breve <div if.bind="expression"> rende il div parte della pagina o meno a seconda del risultato di expression.

Risolto il problema della visibilità, passiamo all'esecuzione del metodo join. In questo caso di tratta di reagire ad un evento del DOM per invocare un metodo del ViewModel. Per questo scenario Aurelia mette a disposizione l'event binding.

L'event binding sfrutta la stessa sintassi dell'attribute binding per consentirci di intercettare gli eventi che vengono lanciati dagli elementi del DOM. Le uniche differenze sono nei comandi supportati (trigger e delegate per l'event binding) e nella proprietà speciale $event che può essere usata nell'espressione JavaScript per ottenere un riferimento al DOM event originale. Anche in questo caso rimandiamo l'approfondimento dei due comandi, utilizzando al momento il delegate in maniera del tutto fideistica. Ad esempio <button click.delegate="run()">Esegui</button> per eseguire il metodo run in risposta al click sul button Esegui. Nel nostro caso quindi:

L'ultima cosa che ci resta da fare per completare la "tile" della partita è colorare il panel di verde o di rosso. Si tratta in questo caso di aggiungere il class panel-success o quello panel-danger al div che definisce la "tile" (cioè quello con class panel). Per farlo useremo ancora una volta la string interpolation utilizzando in questo caso un'espressione:

Ora che il frammento di HTML che descrive la partita è completo, non ci resta che iterare tutti gli elementi che compongono il nostro array e clonare il frammento iniettandolo di volta in volta nel DOM. Come già detto nel caso della visibilità, HTML non fornisce ovviamente un supporto nativo per questo scenario; non esiste quindi un attributo o un tag specifico per gestire questa operazione. Ecco che arriva in nostro soccorso il secondo custom attribute cui facevo riferimento in precedenza. Si tratta del repeat.for, per mezzo del quale possiamo indicare al framework una collezione su cui vogliamo iterare usando (manco a dirlo) la sintassi standard di ES2015 per il comando for, ossia item of collection. Aurelia prende automaticamente l'elemento del DOM su cui abbiamo apposto l'attributo come template per i vari elementi della lista e si occupa di clonare e iniettare nel DOM l'HTML risultante. In definitiva:

Non ci resta che il button che determina la creazione di una nuova partita. Anche in questo caso possiamo usare l'event binding scrivendo:

Prima di poter eseguire il nostro codice c'è un'altra operazione che dobbiamo portare a termine. Nello screenshot del post precedente abbiamo visto che ho utilizzato Bootstrap per migliorare un po' (nei limiti imposti dalla mia incompetenza di grafico) il look dell'applicazione. In primis dobbiamo quindi importare il relativo package da JSPM seguendo la procedura già descritta per i package di Aurelia. Fatto ciò dobbiamo fare in modo che il css di Bootstrap sia caricato insieme alla nostra applicazione. Possiamo ottenere questo risultato in tre modi:

  1. Aggiungendo un tag link che punti al css nella pagina principale index.html in modo che sia caricato non appena atterriamo sull'entry point della nostra applicazione:
  2. Usando un tag require che punti al css all'interno della nostra View app.html in modo da comunicare ad Aurelia (e quindi a JSPM) che abbiamo bisogno di quel file e che deve essere dinamicamente caricato (se non è già stato fatto):
  3. Specificando il css come risorsa globale durante la configurazione di Aurelia:

Trattandosi di una risorsa non specifica della View, ma piuttosto necessaria a livello di intera applicazione, la soluzione ideale sarebbe la terza (in realtà ci sarebbe anche una quarta opzione ma non facciamo confusione al momento). Visto però che non abbiamo ancora approfondito la configurazione, andrò per ora con la seconda soluzione.

Ed ora, F5 e via!

Prima di concludere voglio spendere due parole sul frammento di HTML che abbiamo usato come template della partita. Abbiamo visto in un post precedente che ogni View in Aurelia è un template HTML5 introdotto dall'apposito tag template. Nulla ci vieta, e anzi in alcuni casi prossimamente useremo proprio questo approccio, di estrarre il div che descrive la partita in un file esterno, strutturato a sua volta come template HTML5 (cioè incorporato in un tag template). A questo punto possiamo istruire Aurelia ad usare quel template per popolare la parte di pagina relativa alla partita, potendo allo stesso tempo riusare lo stesso file in altre pagine in cui abbiamo bisogno della stessa UI (con un meccanismo in parte simile a quello delle partial view di MVC). Questo meccanismo è detto in Aurelia composition e, come vedremo in un prossimo post, consente di comporre la propria pagina con un alto grado di flessibilità.

Happy coding!

P.S. Il codice sorgente è disponibile su GitHub, basta scaricare la release con lo stesso nome del post (Binding di base in questo caso).

Aurelia e ASP.NET Core: fondamenti di binding

Questo post fa parte di una serie dedicata ad Aurelia e ASP.NET Core.

Se fino ad oggi abbiamo navigato sotto costa, è arrivato il momento di rompere gli indugi e puntare la prua al largo. Nel proseguo della nostra esplorazione dell'universo Aurelia proverò a costruire un'applicazione concreta, per evitare di cadere nel solito tranello delle presentazioni apparentemente perfette che poi crollano al primo caso reale. Io realizzerò un'applicazione per supportare on-line un gioco di ruolo, voi potete ovviamente seguire la stessa strada o realizzare qualcosa di diverso.

I requisiti della nostra applicazione

Prima di buttarci nel codice, devo necessariamente spendere due parole per chiarire il dominio applicativo: per chi non lo sapesse un gioco di ruolo è quello in cui una serie di giocatori si calano nella parte di personaggi immaginari e vivono le avventure (generalmente in stile fantasy) narrate da un altro giocatore, detto master, che rappresenta il deus ex-machina della storia. Nel mio caso sto parlando del GdR "originale", in cui i giocatori si riuniscono attorno ad un tavolo e il master racconta la storia e dipana le vicende. Si dà però il caso che nel frattempo i giocatori hanno messo su famiglia e non possono più vedersi con la facilità di prima. Urge quindi trasformare le sessioni reali in sessioni virtuali (su Skype) e urge un supporto informatico per tenere traccia dell'evoluzione dei personaggi (l'equivalente digitale della scheda cartacea).

Ora che abbiamo chiaro il contesto, vediamo quali sono i primi requisiti dell'applicazione:

  • Come ospite, voglio diventare membro della community per poter giocare;
  • Come membro, voglio autenticarmi per poter accedere ai miei dati e alle mie partite;
  • Come utente, voglio vedere la lista delle partite cui sto partecipando;
  • Come utente, voglio iniziare una nuova partita come master;
  • Come utente, voglio unirmi ad una partita in corso come giocatore.

Cominciare gli sviluppi dai primi due requisiti, oltre che essere tremendamente noioso, ci obbligherebbe ad un nuovo percorso teorico di studio delle modalità di autenticazione/autorizzazione di una SPA, tenendo anche conto che non ho alcuna intenzione di gestire registrazioni, nomi utenti e password ma voglio invece sfruttare i servizi offerti dai principali provider di autenticazione (Facebook, Google, Microsoft, …). Dato che vogliamo fermamente mettere un primo abbozzo dell'applicazione sotto l'albero di Natale, daremo i primi due requisiti per acquisiti e cominceremo a sviluppare dal terzo.

Per iniziare assumiamo quindi di avere un utente registrato ed autenticato che nella sua home page voglia: a) vedere la lista delle partite cui è coinvolto; b) dare inizio ad una nuova partita e c) unirsi ad una partita in corso.

Per quanto detto fino adesso, dovrebbe essere ormai chiaro che un'applicazione Aurelia si compone di due componenti: la View, che contiene il template grafico da utilizzare, e il ViewModel, che contiene i dati con cui idratare tale template per comporre la pagina finale. Un'ipotetica pagina di visualizzazione di un utente, ad esempio, avrà un certo template grafico (sempre uguale) che sarà poi riempito di volta in volta con i dati specifici del particolare utente.

Nel nostro caso, come prima cosa abbiamo quindi bisogno di un file JavaScript che conterrà le informazioni sulle partite, e di un file HTML che avrà lo scheletro della UI da far vedere all'utente. Abbiamo visto che per convenzione Aurelia carica una pagina che corrisponde alla coppia app.js/app.html. Non ci vogliamo ancora addentrare nei gorghi della configurazione, quindi per ora ci limiteremo a riusare gli stessi due file dell'esempio precedente, semplicemente aggiornandone il contenuto.

Il primo ViewModel

Se vogliamo mostrare un elenco di partite, un buon punto di partenza è avere un array contenente i dati delle stesse. In futuro tali dati arriveranno da una chiamata ad un apposito servizio ASP.NET Core, per il momento è più che sufficiente dichiarare un array hard-coded nel costruttore della classe che rappresenta il nostro ViewModel usando la sintassi ES2015:

Cominciamo con il descrivere una partita con un modello molto semplice che arricchiremo nel corso del tempo: Un identificativo univoco della partita (id), il nome della stessa (title), il nome del giocatore che svolge le funzioni di master (master) e due flag che ci dicono se possiamo unirci (canJoin) o se possiamo partecipare alla sessione di gioco corrente (canPlay).

Come vedete non c'è alcun obbligo di chiamare la classe con lo stesso nome del file (anche se è buona norma per i ViewModel per non generare confusione). Dato che già so che "a regime" la nostra coppia si chiamerà games.js/games.html, ho chiamato la classe direttamente Games.

Oltre ai dati sulle partite esistenti, per soddisfare gli ultimi due requisiti abbiamo bisogno dei due metodi per creare una nuova partita e per unirsi ad una partita esistente. Anche in questo caso ci limitiamo momentaneamente a due metodi fake con il solo scopo di verificare il funzionamento della pagina:

La prima View

Per capire come definire il template diamo prima un'occhiata al risultato finale che vogliamo ottenere:

Vogliamo quindi un panel bootstrap per ogni partita, il cui header sarà uguale al nome della partita (title) e il cui body riporterà il nome del master (master). Un button sempre nel body ci consentirà di unirsi alla partita (canJoin = true) o di accedere alla stessa (canPlay = true), mentre un altro button fuori dall'elenco permetterà di creare una nuova partita. Le partite "giocabili" saranno evidenziate in verde, le altre in rosso. Il frammento di HTML necessario ad ottenere il risultato di cui sopra (al netto delle informazioni sulle singole partite) è questo:

In questa porzione di HTML dobbiamo inserire il title (1) e il master (2). Dobbiamo inoltre fare in modo che il button (3) sia visibile solo se canJoin = true e che il suo click esegua il metodo join del ViewModel e che il link (4) sia invece visibile se canPlay = true. A seconda del valore di canPlay dobbiamo quindi aggiungere il class panel-success o quello panel-danger al panel (5). Tutto ciò va ovviamente ripetuto per ogni partita del nostro elenco duplicando l'intero div (6). Infine dobbiamo chiamare il metodo create del ViewModel al click sul button (7).

Cos'è il binding?

Il problema diventa quindi quale meccanismo utilizzare per gestire questo travaso di dati dal ViewModel alla View.

L'approccio più semplice è ovviamente dato da un'interazione "forte" tra le due parti. Il ViewModel conosce perfettamente la struttura della View ed è in grado di individuare le parti che devono essere idratate e di iniettare nelle stesse i valori presenti al suo interno (ad esempio utilizzando jQuery con una sintassi tipo $('#title').html(this.title). Questa operazione può essere tipicamente portata a termine al momento del caricamento, fornendo all'utente una pagina già completa di tutte le informazioni.

Le cose però si complicano quando dobbiamo gestire la visibilità di alcuni elementi o quando dobbiamo gestire una lista con la conseguente necessità di clonare e iniettare nuove porzioni di HTML. Ancor peggio quando il passaggio delle informazioni deve essere bidirezionale, quando cioè le informazioni aggiornate dall'utente sulla View devono essere ritrasmesse al ViewModel, tipicamente per dare avvio ad un processo di business o ad una semplice operazione di salvataggio delle stesse. Nasce infatti l'esigenza di individuare il momento in cui riportare le informazioni sul ViewModel, intercettando una serie di eventi sulla pagina (come il click sul pulsante di submit o la digitazione in un campo di input), e in breve tempo la gestione di questi scambi di dati può diventare molto complessa e tremendamente fragile per via dello stretto collegamento tra le due parti che costringe a tenere allineato il ViewModel ad ogni cambiamento della View.

Per ovviare a questa serie di problematiche, i framework che si basano sul pattern MVVM (come Aurelia) utilizzano un meccanismo detto binding. In cosa consiste? In parole povere si tratta di una sintassi specifica che consente di specificare all'interno della View i punti in cui è necessaria l'interazione con un corrispettivo elemento del ViewModel, che sia il valore di una proprietà che deve essere riportato sulla UI, un metodo che deve essere invocato al click di un pulsante o ancora un flag che determina la visibilità o meno di un elemento grafico. La sintassi è ovviamente riconosciuta dal framework che è quindi in grado di processare la View, individuare i punti di interazione e idratare la UI con i valori provenienti dal ViewModel. In aggiunta il framework si prende anche carico della successiva gestione della vita della View, intercettando gli eventi scatenati dall'interazione dell'utente, modificando il ViewModel e di riflesso aggiornando ulteriormente la View stessa. Tutto ciò potendo mantenere il ViewModel del tutto agnostico rispetto alla View.

Nel prossimo post vedremo quali strumenti Aurelia ci mette a disposizione per gestire il binding.

Merry coding and happy new year!

Aurelia e ASP.NET Core: convention-over-configuration

Questo post fa parte di una serie dedicata ad Aurelia e ASP.NET Core.

Al termine di un precedente post ci eravamo lasciati con tre domande cui stiamo rispondendo prima di procedere.

Come ha fatto Aurelia a capire che volevamo mostrare il contenuto della pagina app?

Nel primo post di questa serie avevamo accennato al fatto che Aurelia è fortemente basato sul concetto di convention-over-configuration. Di che si tratta? In termini estremamente semplici l'idea si può così riassumere: in una qualsiasi applicazione (sia essa basata su SPA come in Aurelia o su un altro approccio, tipo MVC) ci troviamo costantemente ad affrontare problemi architetturali generici e comuni, come ad esempio banalmente, in che modo registriamo sul framework i componenti che vogliamo includere? E in che modo gli comunichiamo l'associazione tra una View e il suo ViewModel? Alcuni framework sono molto cerimoniosi da questo punto di vista e richiedono lunghe configurazioni che a volte trascendono nella puntualizzazione dell'ovvio. Aurelia, come altri framework basati sullo stesso concetto, cerca invece di "supporre" in maniera intelligente quello che potremmo aver necessità di fare applicando in autonomia una serie di convenzioni tratte dall'esperienza. Che succede se abbiamo esigenze specifiche che mal si conciliano con le convenzioni definite da Aurelia? Nessun problema, possiamo facilmente esplicitare una diversa configurazione e Aurelia ne terrà conto (ma vi assicuro che le convenzioni di Aurelia sono assolutamente sensate e logiche).

Già nel nostro primo semplicissimo esempio ci siamo imbattuti in questo approccio, e ancora avremo modo di farlo proseguendo nel nostro viaggio. Cosa è successo dietro le quinte che ha consentito alla pagina app di essere mostrata senza una nostra specifica indicazione? Cogliamo l'occasione per familiarizzare con il codice di Aurelia e addentriamoci nel framework per risolvere il "mistero".

Il codice contenuto nel file index.html è ridotto veramente al minimo. È quindi chiaro che se c'è della magia, questa è contenuta nel bootstrapper che viene caricato con la chiamata System.import('aurelia-bootstrapper').

Potremmo quindi aprire il file aurelia-bootstrapper.js che si trova in wwwroot/_libs/npm/aurelia-bootstrapper@1.0.1 ma ci troveremmo davanti a codice ES5. Il pacchetto presente su NPM e scaricato da JSPM è infatti già convertito in ES5 per garantire la massima compatibilità con i browser attualmente sul mercato. Nulla ci vieta ovviamente di cercare la soluzione qui, ma io andrò invece alla fonte, direttamente su GitHub, dove si trova il file originale in ES2015.

Il file si compone di una serie di variabili statiche e funzioni definite secondo la sintassi ES2015. Nulla di tutto ciò comporta esecuzione di codice al momento del caricamento. Ma se andiamo proprio in fondo al file, troviamo la dichiarazione di una constante starting il cui valore è pari al risultato della chiamata alla funzione run (manco a dirlo un oggetto che rappresenta una promise):

È questa quindi la funzione che viene eseguita immediatamente al caricamento del modulo e che determina l'avvio di Aurelia. Dopo aver eseguito un po' di codice di inizializzazione, la funzione run cerca nel documento i tag (nel nostro caso il body) che abbiamo marcato con l'attributo aurelia-app (ricorderete che nel precedente post avevamo detto che questo attributo serve ad indicare ad Aurelia in quale parte della pagina vogliamo caricare l'applicazione).

A giudicare dal codice sembrerebbe che Aurelia sia in grado di gestire contemporaneamente diverse applicazioni completamenti distinte e indipendenti. Così su due piedi non mi vengono in mente scenari in cui ciò potrebbe essere necessario, in ogni caso è un ulteriore attestato di quella flessibilità del framework che avremo modo di apprezzare più in concreto in altre situazioni. Per ogni attributo trovato viene chiamato il metodo handleApp per gestire l'inizializzazione dell'applicazione:

La funziona handleApp si limita a recuperare il valore dell'attributo aurelia-app e a passarlo alla funzione config che si occupa della configurazione:

Il parametro viene ricevuto dalla funzione config con il nome di configModuleId e memorizzato nella proprietà omonima dell'oggetto Aurelia appena creato. Questo ci fa capire cosa si aspetta Aurelia da quel modulo (nel caso in cui, come avverrà in futuro, decidessimo di specificarne il nome nell'attributo aurelia-app): una classe tramite la quale configurare il framework a nostro piacimento.

In ogni caso, visto che non abbiamo specificato un valore per l'attributo, aurelia.configModuleId sarà null, la porzione di codice subito sottostante sarà ignorata e, al termine della procedura di avvio (cioè nel ramo then della promise ritornata da start), il controllo passerà al metodo setRoot:

Il metodo setRoot è definito all'interno della classe Aurelia che fa parte del package aurelia-framework e si occupa di caricare la prima pagina dell'applicazione. Il metodo si aspetta un parametro opzionale root che di default è null (convention) il che ci fa capire che in altri scenari potremo passare espressamente la pagina che vogliamo sia l'entry point della navigazione (configuration). Visto che nel nostro caso root è null e che this.configModuleId è parimenti null (per quanto visto sopra), il metodo imposta la root ad "app" (convention) e poi la utilizza come primo ViewModel da caricare:

L'arcano è dunque risolto, abbiamo preso confidenza con il codice interno di Aurelia, e abbiamo anche avuto modo di vedere che il modulo che definisce la classe base del framework è scritto in TypeScript (come avrete notato dalla tipizzazione dei parametri e dal generic applicato alla Promise di ritorno).

Le prove tecniche di navigazione sono terminate, dal prossimo post prenderemo il largo sul serio.

Happy coding!

Aurelia e ASP.NET Core: i template di HTML5

Questo post fa parte di una serie dedicata ad Aurelia e ASP.NET Core.

Al termine di un precedente post ci eravamo lasciati con tre domande cui stiamo rispondendo prima di procedere.

Che fine ha fatto il tag template che era nel file app.html?

Nell'esempio del post avevamo creato un semplicissimo file che conteneva un frammento di HTML con un tag template. Nella pagina inviata al browser tale tag era però sparito. Come mai?

Nel primo post di questa serie avevamo detto che Aurelia è stato progetto cercando il massimo rispetto degli standard HTML e ECMAScript esistenti. Quando per il funzionamento del framework c'è bisogno di un costrutto o di una sintassi dedicata, la scelta del Team è stata quella di inventare il meno possibile, riutilizzando se possibile quanto già standardizzato dalle ultime specifiche.

Nel caso delle View (come la pagina app.html del nostro esempio) la scelta è stata quindi l'utilizzo della sintassi HTML5 per i Template. Di cosa si tratta?

Nelle specifiche HTML precedenti alla versione 5 ha sempre spiccato la mancanza di un meccanismo di templating nativo. Stiamo parlando in sostanza dell'equivalente di ciò che è Razor per il mondo ASP.NET o Django per quello Python. L'esigenza è in definitiva quella di inviare al browser un frammento di HTML da non renderizzare direttamente ma da usare come stampo per creare porzioni di pagina più grandi e tipicamente uguali tra loro (a meno ovviamente dei dati specifici di ogni singola componente). Per capire meglio di cosa stiamo parlando, potremmo pensare ad una tabella composta da tante righe, tutte ovviamente uguali per quando attiene a struttura, layout e stili, con la sola differenza delle informazioni riportate in ognuna di esse.

Come detto fino ad oggi le specifiche non prevedevano una soluzione standard per tale necessità, e gli espedienti comunemente più usati (sia dai singoli programmatori che dalle librerie di componenti più diffuse) consistevano nell'incapsulare tali frammenti in tag script marcati con un attributo type personalizzato (come ad esempio <script type="text/x-my-templates">…</script>) o in elementi del DOM nascosti (<div style="display: none">…</div>). In tale modo era possibile poi individuarli, manipolarli e aggiunti alla pagina dinamicamente tramite JavaScript.

Entrambe queste soluzioni sono caratterizzate da limiti e difetti che sono stati fortunatamente superati in HTML5, grazie alla definizione di un nuovo tag dedicato, il template appunto.

Il vantaggio principale del tag template è dato dal fatto che, pur essendo presente sulla pagina, è del tutto inerte fino a quando non viene clonato e iniettato nuovamente nella pagina stessa sotto forma di frammento di HTML. Inerte vuol dire che il contenuto del template non viene processato al momento del caricamento, ma solo al momento della clonazione. Se ad esempio nel contenuto è presente un tag script, il codice JavaScript all'interno dello stesso non viene eseguito se non in fase di clonazione. In quello stesso momento sarà caricata ad esempio l'immagine cui fa riferimento l'attributo src di un eventuale tag img.

Se modificassimo il file app.html del post precedente in questo modo:

e provassimo poi a caricare il file direttamente in un browser pienamente compatibile con le specifiche HTML5 relative ai template (Chrome nel mio caso), potremmo verificare che né il contenuto HTML né il codice JavaScript della pagina vengono eseguiti al momento del caricamento:

Per rinforzare questa separazione tra la pagina e il template, anche i normali meccanismi di ricerca degli elementi del DOM, come l'arcinoto document.getElementById, non funzionano sugli elementi che fanno parte del contenuto del template, in modo da evitare che si possa, anche involontariamente, interagire con esso.

I Template fanno parte di un gruppo di quattro nuove tecnologie definite nelle specifiche di HTML5 (insieme a Custom Element, Shadow DOM e Import) che sono alla base del concetto di Web Component che avremo modo di approfondire più avanti quando parleremo di tecniche avanzate di composizione delle pagine in Aurelia.

Per ora ci basta sapere che tutte le nostre View saranno frammenti di HTML inclusi in un tag template da cui erediteranno proprietà e comportamenti. Al momento opportuno Aurelia si occuperà di recuperare il template, idratarlo con i dati provenienti dal ViewModel e iniettarlo nella posizione corretta del DOM.

Happy coding!

Aurelia e ASP.NET Core: moduli, classi e promise in ES2015

Questo post fa parte di una serie dedicata ad Aurelia e ASP.NET Core.

Al termine dell'ultimo post ci siamo lasciati con tre domande cui cercheremo di rispondere in tre post ravvicinati nei prossimi giorni.

Cos'è la strana sintassi che abbiamo usato nel file app.js?

Nel primo post di questa serie avevamo accennato al fatto che Aurelia è scritto interamente in ES2016, l'ultimissima versione delle specifiche JavaScript che va ulteriormente ad estendere il già innovativo ES2015 (per lungo tempo altrimenti noto come ES6).

Descrivere, anche in modo sommario, tutte le nuove caratteristiche di ES2015/6 ci farebbe deviare troppo dalla nostra rotta. Userò quindi il "solito" approccio di limitarmi a descrivere quello che ci è realmente utile al momento, lasciando eventuali ulteriori approfondimenti ad un prossimo post.

Classi

ES2015 introduce in JavaScript il concetto di classe. Proprio come in C# o in altri linguaggi ad oggetti, una classe è una matrice per creare oggetti: ogni oggetto al momento della creazione sarà fornito degli attributi (proprietà) e dei comportamenti (metodi) definiti nella classe.

Il concetto di oggetto non è ovviamente nuovo in JavaScript ma per via della complessità nella sua gestione a 360 gradi e dell'approccio radicalmente diverso dagli altri linguaggi ad oggetti, le nuove specifiche hanno aggiunto un po' di "syntactic sugar" per mezzo della nuova keyword class. Creare un oggetto in JavaScript è quindi ora molto simile alla sua controparte C#: si definisce una classe e si istanzia l'oggetto sempre usando new. La definizione delle proprietà della classe avviene direttamente all'interno del costruttore mentre i metodi possono essere aggiunti al corpo (FYI ES2016 ha ulteriormente esteso questo nuovo costrutto dando la possibilità di definire anche le proprietà nel corpo della classe, in maniera più simile a quanto facciamo con C#):

Con il nuovo costrutto, l'ereditarietà viene gestita per mezzo della keyword specifica extends:

Ho già avuto modo di dire che si tratta solo di "syntactic sugar". Dietro le quindi il funzionamento è ancora quello di ES5, basato su funzioni e prototype. Ma di questo parleremo magari meglio in un post dedicato, fuori da questa serie su Aurelia. In questo contesto ci basti sapere che questa sarà la sintassi che useremo ogni volta che dovremo definire un ViewModel da associare ad una View. Ma il pur banale ViewModel che abbiamo costruito presentava un'altra keyword "strana": export. Di che si tratta?

Moduli

Da sempre JavaScript soffre di problemi di scarsa organizzazione del codice e mancanza di controllo sulla visibilità dello stesso. Per farla breve:

  • Tutto ciò che viene definito (funzioni, variabili, ecc.) diventa un attributo dell'oggetto globale window il che, oltre a generare una certa confusione, espone le applicazioni a conflitti sui nomi quando si usano librerie provenienti da fonti diverse (che come sappiamo in JavaScript è la norma);
  • Come se non bastasse, tutto il codice scritto è esposto pubblicamente, non c'è modo di definire logiche interne ad un oggetto come facciamo ad esempio con i metodi privati in C#, e la dinamicità del linguaggio (inclusa la possibilità di sostituire il corpo di una funzione con codice completamente diverso a runtime con una semplice assegnazione) non fa che peggiorare le cose;
  • Spesso parti di codice definite in file o librerie diverse dipendono l'una dall'altra e non è sempre immediato garantire che il file che contiene una certa funzione venga caricato prima di quello che la utilizza, causando in qualche occasione eccezioni non previste.

Ovviamente la comunità di sviluppatori JavaScript non è stata semplicemente ferma a subire queste lacune del linguaggio ma ha piuttosto cercato forme alternative per ottenere il risultato desiderato (e come ormai dovrebbe essere chiaro in un linguaggio dinamico come JavaScript c'è quasi sempre un modo, magari non proprio ortodosso, per raggiungere il proprio scopo).

Dopo un primo tentativo di mitigazione tramite le cosidette Immediately-Invoked Function Expression (IIFE), una seconda risposta è arrivata dalle specifiche CommonJS e da quelle AMD (Asyncronous Module Definition) su cui si basa RequireJS. Alla base di questo nuovo approccio ci sono i concetti di modulo come unità indipendente e quelli di esportazione e importazione del contenuto dello stesso per definire i livelli di visibilità e le dipendenze tra i moduli.

ES2015 contribuisce a standardizzare quelle specifiche attraverso l'introduzione di due nuove keyword: export e import appunto. Ogni file JavaScript costruito secondo le specifiche costituisce un modulo a sé stante i cui elementi interni (variabili, funzioni, classi) vivono in isolamento rispetto agli altri moduli e al contesto globale. Gli elementi del modulo (uno o più) che si vogliono rendere pubblici devono essere esplicitamente marcati con la keyword export, come abbiamo fatto nel post precedente nel file app.js, e importati nel modulo che ne vuole usufruire (come si vede dalle "ondine" rosse, Visual Studio 2015 ad oggi non digerisce molto bene la sintassi ES2015 nel file js):

Aurelia si occupa di caricare i moduli che compongono i ViewModel automaticamente quando sia necessario il render della corrispettiva View, quindi tipicamente non avremo bisogno di importare i ViewModel nella nostra applicazione. Avremo invece bisogno di importare i moduli JavaScript che useremo ad esempio per incapsulare il codice di interfacciamento con i nostri servizi di back-end.

Promise

C'è un ultimo aspetto di ES2015 di cui dobbiamo parlare anche se non l'abbiamo incontrato nel primo semplicissimo ViewModel. Aurelia è infatti fortemente basato sulla programmazione asincrona. La quasi totalità delle funzioni del framework è progettata per ritornare immediatamente il controllo al chiamante mentre prosegue l'esecuzione delle operazioni, salvo poi notificare il completamento delle stesse a tempo debito.

Il concetto di programmazione asincrona è ben noto agli sviluppatori web per via della latenza intrinseca nell'architettura client/server basata su HTTP. Ogni volta che comunichiamo con il server per ottenere delle informazioni, non possiamo fare assunzioni sui tempi di risposta e quindi, se non vogliamo bloccare completamente l'esecuzione dell'applicazione, è buona norma tenere conto di queste limitazioni e progettarla di conseguenza.

Il modo "tradizionale" di gestire la programmazione asincrona in JavaScript (e non solo) era basato sul meccanismo delle callback. Una funzione che doveva eseguire operazioni potenzialmente molto lunghe prevedeva tra i propri parametri una funzione da chiamare al termine delle operazioni. In questo modo era possibile ritornare immediatamente il controllo al chiamante ed eseguire poi il codice in risposta al completamento nella funzione di callback.

Non mi dilungherò sui tanti problemi che questo comporta. Trovate tonnellate di documentazione in rete (tra cui ad esempio le slide della mia sessione a .NET Campus). In ogni caso per superare tali limitazioni già da diversi anni si è diffuso l'utilizzo delle promise come alternativa alle callback.

Anche in questo caso infatti, come per le classi e i moduli, ES2015 arriva a standardizzare un approccio già in voga tra gli sviluppatori, con diverse librerie che fornivano un'implementazione delle specifiche Promises/A+ come ad esempio Q.

Una promise è in definitiva (e con un'innocente semplificazione) un oggetto che rappresenta un valore che al momento non è noto ma che lo sarà prima o poi nel futuro. Come interagiamo con tale oggetto? Fondamentalmente per mezzo del suo metodo then – che da specifiche ogni promise deve obbligatoriamente implementare– che accetta come parametri le due funzioni che saranno invocate rispettivamente quando la promise viene risolta positivamente o quando viene viceversa rigettata (ad esempio per un timeout).

Molto spesso del corso dei prossimi post ci imbatteremo quindi in codice come il seguente:

Due cose interessanti vanno aggiunte sul funzionamento di questo meccanismo: intanto il metodo then ritorna a sua volta una promise usando il valore di ritorno della nostra funzione per risolvere la stessa o intercettando un'eventuale eccezione lanciata dal nostro codice per rigettarla. Questo vuol dire che possiamo concatenare chiamate al metodo then con una sintassi molto più chiara e interpretabile della "pyramid of doom" caratteristica delle callback. In secondo luogo, essendo oggetti, le promise possono essere sia memorizzate in una variabile che passate da una funzione all'altra, aprendo la porta a scenari molto interessanti che avremo modo di scoprire nei prossimi post:

E TypeScript in tutto ciò?

Aurelia supporta non solo ES5 e ES2015/6 ma anche TypeScript. Ciò vuol dire che possiamo scrivere la nostra applicazione nel modo che preferiamo, anche se il consiglio è ovviamente di evitare ES5 e orientarsi per una delle due opzioni più "moderne".

Nel corso di questa serie userò principalmente ES2015, ma non escludo di fare qualche esperimento anche con TypeScript più avanti. La differenza in realtà non è più così rilevante come era tra ES5 e TypeScript.

Le aggiunte più interessanti di quest'ultimo rispetto a ES2015 sono a mio parere la type safety (cioè la possibilità di definire quale sia il tipo di variabili, parametri e valori di ritorno), i generic (praticamente uguali anche nella sintassi a quelli di C# e molto utili in diverse situazioni) e la possibilità di definire interfacce. Tutte cose utili ma non indispensabili per il proseguo del nostro progetto.

Happy coding!

Aurelia e ASP.NET Core: hello Aurelia!

Questo post fa parte di una serie dedicata ad Aurelia e ASP.NET Core.

Quando ormai il morale della ciurma cominciava a vacillare, ecco finalmente un urlo straziato dalla coffa: Aurelia!!!

Ebbene sì, siamo giunti finalmente al punto in cui siamo pronti ad aggiungere Aurelia al nostro progetto utilizzando ovviamente NPM e JSPM.

Aggiungere NPM

Per prima cosa dobbiamo quindi aggiungere NPM e, con il supporto fornito da Visual Studio, la cosa è estremamente facile. Ci è sufficiente aggiungere un nuovo npm Configuration File al progetto, di tutto il resto si occupa l'ambiente di sviluppo:

Il file – package.json - è di per se stesso abbastanza scarno e possiamo per ora concentrare la nostra attenzione sulla sezione devDependencies dove è possibile specificare le dipendenze da quei pacchetti che sono utili nella fase di sviluppo del progetto, esattamente il caso di JSPM:

Aggiungere JSPM

L'aggiunta di JSPM è infatti ancora più semplice: come abbiamo visto qualche post fa per il nuovo project.json – che contiene le dipendenze NuGet (ma non vi abituate troppo, in Visual Studio 2017 andrà purtroppo in pensione prematura) – anche package.json supporta l'editing manuale con Intellisense, per cui tutto quello che dobbiamo fare è digitare "jspm" all'interno della sezione devDependencies e selezionare la versione che ci interessa referenziare (al momento la 0.16.48). In seguito al salvataggio del file, Visual Studio si occuperà di scaricare il pacchetto e le eventuali relative dipendenze:

Con un po' di pazienza avremo alla fine JSPM elencato tra le dipendenze del progetto. Al momento vi sconsiglio di cliccare sulla freccia accanto al pacchetto JSPM, perché è come aprire il vaso di Pandora (e non fate come Julia Roberts in Nothing Hill… non aprite quella porta):

Aggiungere Aurelia

JSPM deve essere configurato prima di poter essere usato, ma tale inizializzazione può essere fatta congiuntamente con il download del primo pacchetto, ed è esattamente ciò che faremo.

Visual Studio non dispone (ad oggi) di un supporto nativo per JSPM come per NPM o Bower. Come conseguenza, l'installazione dei vari pacchetti dovrebbe essere fatta a riga di comando. In alternativa vi suggerisco di scaricare la comodissima estensione partorita dalla mente sempre viva di Mads Kristensen: si tratta in un Package Installer che consente di installare pacchetti da diverse fonti, wrappando in pratica le chiamate a riga di comando in una maschera semplice ma efficacie:

A seguito dell'installazione dell'estensione avremo disponibile una nuova voce Quick Install Package… nel menu contestuale del progetto (attivabile anche con lo shortcut Shift+Alt+0):

Clicchiamo sulla voce e nella maschera risultante selezioniamo JSPM come fonte, digitiamo "aurelia-framework" come pacchetto e clicchiamo infine su Install:

Come detto alla prima installazione sarà necessario configurare JSPM rispondendo ad una serie di semplici domande:

  • La prima domanda ci chiede se vogliamo registrare le proprietà di JSPM che saranno aggiunte al file package.json di NPM all'interno di un nodo jspm. Confermiamo l'impostazione di default ;
  • La seconda domanda ci chiede di indicare quale sia la web root del nostro progetto. Per quanto abbiamo detto in un precedente post è ovvio che per i progetti ASP.NET Core dobbiamo impostare ./wwwroot;
  • È la volta di configurare la cartella in cui JPSM scaricherà i pacchetti che aggiungeremo al progetto (come ad esempio in questo caso Aurelia). Il default è wwwroot/jspm_packages. Potete scegliere questa cartella secondo le vostre preferenze, io per ora imposterò wwwroot/_libs;
  • È ora il momento della posizione del file config.js che servirà a JSPM per gestire i pacchetti scaricati. Confermiamo il default wwwroot/config.js (e confermiamo anche la sua successiva creazione in automatico);
  • La sesta domanda ci chiede di indicare la URL con cui il nostro sito è esposto pubblicamente. Confermiamo il default /;
  • Siamo alla fine ed è sufficiente confermare il default anche per le ultime due domande: , vogliamo usare un transpiler (cioè un meccanismo per "invecchiare" il codice JavaScript in modo da renderlo compatibile con i browser più anziani) e in particolare vogliamo usare Babel.

Al termine della configurazione il download del pacchetto aurelia-framework (e di tutti i pacchetti da cui esso dipende, Aurelia è strutturato in maniera molto granulare, come ASP.NET Core) inizierà automaticamente:

Al termine del download, nella cartella _libs o in qualunque altra cartella voi abbiate scelto in fase di configurazione, potremo trovare i file JavaScript che compongono il cuore del framework Aurelia (un secondo vaso di Pandora che per ora ignoriamo come il primo).

Se vi accorgete che qualcosa non vi convince nel modo in cui avete impostato JSPM non è mai troppo tardi per tornare indietro. La configurazione è infatti disponibile in parte del file package.json di NPM e in parte nel file config.js di JSPM (che potrebbe ovviamente chiamarsi diversamente che avete scelto di modificare il nome di default).

Prima di poter vedere Aurelia in azione abbiamo bisogno di installare un secondo pacchetto chiamato aurelia-bootstrapper del quale avremo modo di parlare meglio in un prossimo post. Ormai sappiamo come fare: Shift+Alt+0 e via:

Hello Aurelia!

I preliminari sono finiti (il che vuol dire che tempo 5 minuti e saremo giunti alla conclusione): possiamo mettere in piedi la nostra demo. Come tutti i framework SPA, Aurelia ha bisogno di un punto di ingresso, una URL che venga servita da ASP.NET Core come una qualunque risorsa "server" e che contenga al suo interno il codice necessario al framework per mettersi in azione e prendersi carico delle successive richieste sul "client". Per il nostro esempio riutilizzeremo il file index.html, modificando il body per aggiungere il codice necessario ad attivare Aurelia:

Ci sono un po' di cose da notare in questo frammento di codice:

  1. Il primo tag script serve ad importare la libreria SystemJS che si occupa del caricamento asincrono on-demand dei vari moduli che comporranno la nostra applicazione (quelli di Aurelia, di altre librerie che potremmo voler usare come ad esempio Bootstrap e ovviamente quelli che scriveremo noi stessi per implementare la logica applicativa). Come detto in un precedente post, JSPM integra infatti le funzionalità di un package manager con quelle di un module loader per fornire una soluzione completa;
  2. Il secondo tag script serve ad importate il file di configurazione di JSPM che contiene alcune delle impostazioni che abbiamo selezionato in fase di inizializzazione e, soprattutto, l'elenco dei pacchetti aggiunti all'applicazione con il rispettivo albero di dipendenze;
  3. Il terzo tag script utilizza SystemJS per caricare il bootstrapper di Aurelia che si occuperà di caricare e attivare il framework;
  4. L'attributo aurelia-app con cui abbiano decorato il body serve infine ad indicare ad Aurelia in quale parte della pagina vogliamo caricare la nostra SPA.

Abbiamo già accennato nel post precedente che Aurelia è basato su MVVM. Non ci sorprende quindi che una "pagina" di Aurelia è composta da una View (un file HTML) e da un ViewModel (un file JavaScript). Come vedremo nei prossimi post questa coppia di elementi deve rispettare alcune semplici (e soprattutto logiche) regole nelle quali al momento non voglio addentrarmi perché dovremmo altrimenti ulteriormente rimandare il nostro F5 e finirei col perdere una certa scommessa con un certo personaggio.

Al momento limitiamoci a creare un nuovo file JavaScript app.js sotto wwwroot che funga da ViewModel con il seguente contenuto:

Anche la View (il file HTML app.html) sarà decisamente minimale:

In definitiva il nostro progetto dovrebbe alla fine essere strutturato più o meno così:

Siamo pronti! F5 e via:

Indubbiamente il risultato è quello atteso, ma è evidente che qualcosa di magico deve essere successo dietro le quinte perché quello che abbiamo scritto in index.html, app.js e app.html non è assolutamente sufficiente a garantire quello che vediamo. Se apriamo il DOM Explorer ecco come risulta l'HTML ricevuto dal browser:

Un bel po' di cose sono successe e alcune domande richiedono una risposta:

  • Come ha fatto Aurelia a capire che volevamo mostrare il contenuto della pagina app?
  • Che fine ha fatto il tag template che era nel file app.html?
  • Cos'è la strana sintassi che abbiamo usato nel file app.js?

Le risposte a queste domande si possono sintetizzare in tre concetti – convention-over-configuration, HTML5 standard e ES2016 – che saranno l'argomento principale dei prossimi post.

Happy coding!

 

P.S. Il codice sorgente è disponibile su GitHub, basta scaricare la release con lo stesso nome del post (Hello Aurelia! in questo caso).

«June»
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678