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!

posted @ Wednesday, December 21, 2016 5:35 PM

Print
«August»
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567