Uno dei concetti fondamentali di Angular 2 è il concetto di componente. Un componente è un elemento fondamentale per la costruzione di applicazioni Angular. Esso ha il controllo di una porzione dello schermo implementando sostanzialmente una view.
Facendo riferimento alla versione precedente di Angular, esso assolve i compiti che prima erano delle direttive, dei controller e degli scope. Un unico elemento consente ora di definire gli elementi dell'interfaccia grafica in maniera compatta ed elegante.
Un'applicazione è un insieme di componenti che interagiscono tra di loro. In particolare, i componenti possono essere combinati tra loro creando nuovi componenti organizzati in una struttura gerarchica. Un'applicazione avrà sempre un root component che costituisce in un certo senso il punto d'ingresso dell'applicazione stessa. Per convenzione al root component viene assegnato il nome di AppComponent, ma naturalmente non si tratta di un obbligo.
Alla luce di quanto detto, possiamo rappresentare un'applicazione come un'albero di componenti come quello mostrato dalla seguente figura:
In questo caso abbiamo un'applicazione che mette a disposizione una view per visualizzare un elenco di articoli e una view per la ricerca. La view che mostra l'elenco degli articoli è a sua volta composta da un elenco di view relative a ciascun articolo.
Ma vediamo dal punto di vista del codice come è strutturata un'applicazione Angular 2.
Se esaminiamo la cartella src creata da Angular CLI, troveremo una pagina index.html che rappresenta l'elemento iniziale dell'applicazione. Il suo contenuto è estremamente compatto:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>MyApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
L'elemento rilevante dal punto di vista della nostra applicazione Angular si trova nel body della pagina HTML ed è rappresentanto dal tag <app-root>.
Come possiamo vedere, non abbiamo alcun riferimento a script da caricare all'interno della pagina. In realtà il markup per il caricamento degli script e dei fogli di stile viene iniettato a runtime da Angular-CLI al momento della compilazione dell'applicazione, quindi non dobbiamo preoccuparci di questo.
Ci basta sapere che il punto di ingresso della nostra applicazione sarà il modulo main in cui vengono definite le dipendenze principali e viene avviata l'applicazione. Il codice contenuto del file main.ts è di seguito mostrato:
import './polyfills.ts';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
import { AppModule } from './app/';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
Tralasciamo il codice di supporto alla gestione del progetto (enableProdMode, environment) ed alla compilazione (polyfills.ts) e ci concentriamo sul codice rilevante per l'applicazione Angular.
In particolare vediamo che le funzionalità di Angular vengono importate da una cartella di sistema @angular. Nell'esempio di codice vediamo l'importazione di elementi dal modulo platform-browser-dynamic
e dal modulo core
. Il primo contiene funzionalità specifiche per il bootstrap dell'applicazione all'interno di un browser, mentre il secondo contiene le funzionalità di base di Angular. Avremo modo di scoprire nel corso della guida altri moduli del framework man mano che ne avremo bisogno.
Notiamo anche l'importazione di AppModule dalla cartella app. AppModule rappresenta il modulo di partenza della nostra applicazione, il cosiddetto root module, il quale viene caricato ed avviato invocando il metodo bootstrapModule()
.
Il fatto che abbiamo importato la funzione bootstrap() da uno specifico modulo di sistema, platform-browser-dynamic
, è indice dell'insita modularità dello stesso framework. Infatti non è previsto un solo modo per effettuare il bootstrap di un'applicazione. Quello che abbiamo visto riguarda il bootstrap all'interno di un browser, ma è possibile fare il bootstrap di un'applicazione in ambienti diversi, come ad esempio su un dispositivo mobile, o possiamo creare la nostra modalità di bootstrap personalizzata. In ogni caso, perché possa essere avviata l'applicazione occorre indicare qual è il modulo dell'applicazione da cui partire.
Il root module
Abbiamo visto come l'avvio dell'applicazione Angular d'esempio viene eseguita specificando il modulo AppModule. Un'applicazione può essere composta da più moduli ma può avere un solo modulo di partenza o root module. Per convenzione tale modulo è chiamato AppModule, ma in realtà può avere qualsiasi nome valido per una classe.
Ma cos'è esattamente un modulo in Angular 2? Un modulo è un contenitore di funzionalità che consente di organizzare il codice un'applicazione. Concettualmente esso è abbastanza simile ai moduli in Angular 1.x, anche se la sintassi è molto diversa. Per comprendere come definire un modulo, diamo un'occhiata al codice generato da Angular-CLI per AppModule:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Un modulo non è altro che una classe a cui è stato applicato il decoratore @NgModule. Un decoratore è una funzione che prende una classe e la arricchisce di specifiche funzionalità. Si tratta di uno standard introdotto dalle specifiche ECMAScript 2016 (ES7) e già recepite da TypeScript.
Nel nostro caso, il decoratore @NgModule, importato da @angular/core, arricchisce la classe di metainformazioni che la rendono un modulo. In particolare possiamo notare la presenza delle seguenti metainformazioni:
Metainformazione | Descrizione |
---|---|
declarations | È una lista di componenti ed altre funzionalità che appartengono al modulo corrente: tutti gli elementi dichiarati in questa lista sono disponibili nel modulo senza una esplicita importazione. |
imports | È una lista di elementi importati da altri moduli e resi disponibili nel modulo corrente. |
providers | È un elenco di provider (vedremo in seguito di cosa si tratta) disponibili nel modulo. |
bootstrap | È l'elenco dei componenti da utilizzare come root component dell'applicazione; solitamente la lista contiene un solo elemento. |
L'utilizzo dei moduli è molto articolato e per il momento ci siamo limitati ai concetti fondamentali per comprendere come è costruito il root module. In realtà, oltre alla funzione organizzativa del codice i moduli Angular hanno anche un ruolo per la gestione della dependency injection, della compilazione e del lazy loading, come vedremo più avanti nel corso della guida.
È infatti questo ruolo multiforme che consente di spiegare la differenza tra i moduli di Angular 2 e quelli di ECMAScript 2015. Mentre i primi si pongono ad un livello architetturale dell'applicazione, i secondi si limitano a gestire la visibilità di funzionalità tra blocchi di codice JavaScript.