Guest post: Angular and Typescript. Together forever…


Questo articolo è stato scritto da Andrea Boschin, MVP Windows Platform Development

E’ da un po’ che Typescript è entrato nei miei progetti web, si potrebbe dire portando ordine laddove regnava il caos. In effetti se ripenso a quale fosse la qualità e la manutenibilità del codice nei progetti in cui Javascript regnava sovrano e la paragono a quello che è invece il risultato dell’utilizzo di Typescript mi rendo conto che quest’ultimo ha cambiato completamente il modo in cui sviluppo, l’affidabilità che mi aspetto da questo codice e soprattutto il modo in cui il esso evolve nel tempo. Javascript è un linguaggio potentissimo e dalla sua ha anche l’indiscutibile vantaggio di essere diventato ormai la lingua franca che accomuna dispositivi mobili e workstation desktop, ma per quando ligi e rigorosi si sia nell’applicare regole che aiutino a mantenere una certa qualità del codice, esso non manca di essere fonte di numerosi grattacapi e di una crescente confusione. E’ questo in effetti che sta spingendo notevolmente la comunità degli sviluppatori a prendere sempre più spesso in considerazione Typescript, è questo che riempie le sale delle conferenze di sviluppatori che sentono questo stesso problema e che sono alla indefessa ricerca di una soluzione. Typescript in effetti non solo è uno strumento che consente di applicare un modello di programmazione più rigoroso e maggiormente efficiente, ma porta con il vantaggio di poter sfruttare tutti gli strumenti di Visual Studio quali ad esempio l’Intellisense, ma soprattutto la possibilità di sfruttare la pletora di librerie javascript esistenti con molta semplicità.

A dimostrazione di quello che da un po’ vado professando, in questi giorni è divenuta pubblica la notizia che AngularJS, una popolare libreria Javascript prodotta da Google, ha deciso di adottare Typescript come linguaggio di sviluppo della versione 2.0. La notizia è di certo una conferma dell’efficacia del linguaggio ma non solo. In effetti, il risultato della collaborazione instaurata tra Microsoft e Google sarà che AngularJS contribuirà a migliorare ulteriormente Typescript (i primi risultati di questo sono già attesi nella versione 1.5 del compilatore) e che i file di definizione saranno sempre molto aggiornati. Ma quello che non deve essere dimenticato è che Typescript è già uno strumento efficace che consente di usare a tutti gli effetti Angular proficuamente. Esistono molte ragioni per scegliere questa combinazione. Angular è di certo uno strumento molto raffinato per applicare un pattern di programmazione che separi la logica dall’interfaccia utente, pattern molto simile a MVVM, grazie al fatto che dispone di un raffinato databinding. Essa inoltre fornisce numerosi strumenti per dependency injection e unit testing, consente lo sviluppo di applicazioni Single Page (SPA) e infine supporta lo sviluppatore mediante oggetti quali factories, services e direttive che sono insostituibili per massimizzare il riutilizzo del codice.

Utilizzare Angular con Typescript è molto semplice. Dopo aver installato Typescript 1.4 per Visual Studio 2013 scaricandolo dal sito ufficiale si dovrà creare un progetto web, non necessariamente con ASP.NET. E’ sufficiente in effetti una semplice pagina HTML. A questo punto utilizzando Nuget si dovranno scaricare le seguenti due librerie:

· AngularJS

· Definitely typed for AngularJS

Mentre la prima altro non è che la libreria stessa che sarà scaricata nella cartella Scripts, la seconda è un file con estensione “.d.ts” che verrà copiato in una cartella “typings”. Questo file ha lo scopo fondamentale di istruire il compilatore Typescript dell’esistenza della libreria e dei tipi che possono essere ritrovati al suo interno. Senza di esso saremmo in buona sostanza ciechi ai tipi e il compilatore continuerebbe a darci innumerevoli errori per proprietà e metodi che non conosce. Il suo scopo è in effetti molto simile ai file header del C, ma in questo caso non è necessario includere il file. Basterà la sola presenza di questo file nelle cartelle del progetto web perchè il compilatore lo tenga in dovuta considerazione. Non sarà invece sufficiente ai fini dell’inclusione della libreria Javascript che dovrà per forza di cose avere una referenza all’interno della pagina HTML in cui viene utilizzata, esattamente come si fa normalmente.

<script src=”Scripts/angular.min.js”></script>

Di solito questo caricamento si pone nella parte finale della pagina, subito prima del tag </body>. A questo punto possiamo ipotizzare di creare un file Typescript per ciascuna delle pagine della nostra applicazione. Per ipotesi, ad un file “default.html” facciamo corrispondere un file “default.ts” e aggiungiamo esso stesso come inclusione nella pagina. Per comodità possiamo semplicemente trascinare il file TS nella giusta posizione nella pagina e Visual Studio si occuperà di referenziare il file Javascript risultante dalla compilazione.

<script src=”Scripts/angular.min.js”></script>

<script src=”default.js”></script>

Forse è superfluo dirlo ma è importante notare che l’ordine dei file conta. Infatti dato che il nostro default.ts utilizza Angular quest’ultimo deve essere caricato in precedenza. In una applicazione ben organizzata potremmo vedere una sezione di caricamenti simile alla seguente:

<script src=”Scripts/jquery-2.1.1.min.js”></script>

<script src=”Scripts/moment.min.js”></script>

<script src=”Scripts/angular.min.js”></script>

<script src=”Scripts/bootstrap.min.js”></script>

<script src=”sys.js”></script>

<script src=”sys.services.js”></script>

<script src=”sys.directives.js”></script>

<script src=”default.js”></script>

In questo esempio abbiamo incluso jQuery, MomentJS per la gestione delle date, AngularJS e Bootstrap per i layout responsivi. I file da sys.js in poi sono nostre librerie che contengono utilità comuni a tutte le pagine. Ultimo il file che rappresenta la pagina stessa.

Ora nel tag body inseriamo due attributi:

<body ng-app=”defaultApp” ng-controller=”defaultController”>
   
<script src=”Scripts/jquery-2.1.1.min.js”></script>
   
<script src=”Scripts/moment.min.js”></script>
   
<script src=”Scripts/angular.min.js”></script>
   
<script src=”Scripts/bootstrap.min.js”></script>
    <script src=”sys.js”></script>
    <script src=”sys.services.js”></script>
    <script src=”sys.directives.js”></script>
    <script src=”default.js”></script>
</body>

L’attributo ng-app istruisce Angular che l’applicazione da utilizzare per questa pagina si chiama “defaultApp”. Con ng-controller invece determiniamo che il controller, ovvero la parte di codice che contiene la logica, si chiama “defaultController”. Sarà nel file Typescript che dovremo inizializzare il controller come segue:

angular.module(‘defaultApp’, [])
.controller(‘defaultController’,
[‘$scope’, ($scope) => { $.extend($scope, new DefaultController()); }]);

Con la prima riga viene creata l’applicazione “defaultApp”. In seguito si chiama il metodo controller che dichiara appunto il controller “defaultController”. Questa operazione richiede l’utilizzo di un oggetto denominato “scope”. Lo scope è a tutti gli effetti il connettore tra pagina HTML e Controller. Un po’ l’alter-ego del ViewModel nel pattern MVVM. In buona sostanza tutte le proprietà e i metodi dichiarati al suo interno saranno disponibili nell’html per il databinding. In questo esempio, come mia abitudine faccio uso di jQuery per “fondere” lo scope con una classe Typescript (DefaultController). Questa fusione fa si che lo scope diventi a tutti gli effetti la classe stessa e consenta di lavorare sfruttando al meglio la programmazione orientata agli oggetti e l’Intellisense di Visual Studio. Ecco ora il controller minimale:

class DefaultController
{
    value: number;
    result: number;
    calc(): void
    {
       
this.result = Math.sqrt(this.value);
    }
}

Il controller è molto semplice. La chiamata del metodo calc() esegue la radice quadrata del numero “value” e lo mette in “result”. Ora se usiamo il databinding di Angular possiamo rapidamente visualizzare le informazioni nel body dell’HTML.

<input type=”number” ng-model=”value” value=”2″ />
<button ng-click=”calc();”>Calcola</button>
Math.sqrt({{value}}) = {{result}}

Abbiamo quindi una input type il cui valore è collegato alla variabile “value” mediante la direttiva ng-model. Il pulsante invece usa la direttivsa ng-click per richiamare il metodo “calc()” ed eseguire quindi l’operazione. Infine, nella terza riga i valori di “value” e “result” vengono visualizzati nell’HTML mediante la sintassi che usa le graffe. Questo provocherà la visualizzazione della seguente:

Il tutto è di una semplicità davvero disarmante. Per i programmatori XAML come me questo dovrebbe essere evidentemente molto simile all’utilizzo del pattern MVVM. In verità AngularJS ha dei piccoli vantaggi rispetto all’applicazione del pattern MVVM in C#, come ad esempio il fatto che è possibile creare scope annidati (anche implicitamente come nel caso del binding di liste) ma ciascuno di essi sarà gerarchicamente referenziato allo scope che lo contiene e quindi esisterà una sorta di fallback nelle proprietà che consente di tratte vantaggio da questa gerarchia. Ma questo argomento richiederebbe molto più spazio di quello di questo post. Per approfondimento, vi consiglio di valutare la possibilità di assistere ad una delle mie sessioni sull’argomento, a Mestre il 13/3/2015 (http://www.xedotnet.org/Home/Meeting/20150313) e a Roma il 28/3/2015 (http://rome2015.codemotionworld.com/conference/23/).

Comments (1)

  1. Daniele Morosinotto says:

    Bel post Andrea, sono d'accordo con te sul fatto che Typescript permetta di scrivere codice Javascript "migliore"  più affidabile e controllato; e che usarlo insieme ad un framework ben strutturato come Angular dia ottimi risultati!

    Vorrei solo proporre una piccola modifica alla tua soluzione: ossia utilizzando la sintassi "Controller As" (docs.angularjs.org/…/ngController) nella view sarebbe possibile semplificare ulteriormente il codice di registrazione del controller nel modulo di Angular, rendendo inoltre esplicito nella sintassi di binding all'interno  dell'html a quale controller ci si riferisce nel caso ce ne fossero più di uno, eventualmente anche annidati.

    Per chiarire meglio riporto qui di seguito un veloce esempio delle modifiche da apportare all'HTML in cui ho usato vm come nome esplicito per il tuo DefaultController.

    <body ng-controller="defaultController as vm"> … <button ng-click="vm.calc();" > … {{vm.result}}

    E di conseguenza non sarebbe più necessario usare la mix ($.extend) per fondere lo $scope con la classe TS, ma la registrazione del controller nel modulo di Angular diventerebbe così:

    angular.module("defaultApp",[])

    .controller("defaultController", DefaultController);

Skip to main content