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:
- 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:
- 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):
- 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).