ARCHITECTURE
L'architettura di base si compone dei seguenti building blocks:
- Modules
- Components
- Templates
- Metadata
- Data binding
- Directives
- Services
- Dependency injection
MODULES
Le app Angular sono modulari e il suo sistema di moduli è chiamato "Angular modules or NgModules". Di conseguenza ogni app Angular ha almeno 1 modulo, convenzionalmente chiamato "AppModule".
NgModule è una decorator function che prende in ingresso dei metadata object che in pratica servono a "descrivere" il modulo. I più importanti metatada sono:
- declarations (le classi di tipo "view" per questo modulo come components, directives e pipes)
- exports (sono degli oggetti che esporti da questo modulo e vuoi rendere visibili anche in altri moduli)
- imports (oggetti di altri moduli che vuoi importare in questo modulo)
- providers (tutti i services)
- bootstrap (qui va specificato qual è il modulo Root che fa capo a tutti gli altri)
Di conseguenza la struttura più comune di un modulo sarà:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
COMPONENTS
Il componente è il "controller" di una view dinamica in tutte le sue parti (html, stili, api, js), quindi qui dentro sarà definita la sua component's application logic.
Esempio di Component:
export class HeroListComponent implements OnInit {
heroes: Hero[];
selectedHero: Hero;
constructor(private service: HeroService) { }
ngOnInit() {
this.heroes = this.service.getHeroes();
}
selectHero(hero: Hero) { this.selectedHero = hero; }
}
TEMPLATES
Un template è un HTML che dice ad Angular come renderizzare il componente. Nel template sarà presente del semplice html e del codice tipico di Angular (ngFor, ngIf, ecc).
METADATA
I metadata dicono ad Angular che caratteristiche deve avere una certa classe\modulo. Nel caso dei componenti per esempio "@Component" è un metadata che contiene le informazioni per il component corrente (le più comuni sono: selector, templateUrl e providers). Nello specifico "providers" indica le dipendenze esterne che devono essere iniettate.
DATA BINDING si tratta di un meccanismo per coordinare i dati tra il template html e la parte di logica.
Può essere monodirezionale, come:
{{hero.name}}
<-- Visualizza il valore della proprietà "hero.name"
(click)="selectHero(hero)"
<-- Chiama la funzione "selectHero"
[hero]="selectedHero"
<-- Passa il valore "selectedHero" (sarebbe l'Hero selezionato nel componente padre "HeroListComponent") alla proprietà "hero" del componente figlio "HeroDetailComponent"
Oppure bidirezionale, come:
[(ngModel)]="hero.name"
<-- mette in binding bidirezionale per esempio un input[text] con la sua relativa proprietà del modello.
DIRECTIVES Una direttiva è una classe che usa il decorator "@Directive". Diciamo che possono essere considerate un subset di un component in quanto in pratica un component è una "directive-with-a-template", una sorta di directive estesa con le funzionalità di templating.
Esistono 2 tipi di direttive: structural e attribute.
Le direttive di tipo STRUCTURAL modificano di fatto il layout aggiungendo, rimuovendo o sostituendo elementi nel DOM. Per esempio: *ngFor, *ngIf
Le direttive di tipo ATTRIBUTE modificano lo stile o il comportamento di un elemento esistente. Risultano come semplici attributi html con una sintassi particolare. Per esempio: [(ngModel)]="hero.name"
SERVICES Un servizio può contentere qualsiasi cosa (valore, funzione, funzionalità) che serve alla tua applicazione e tipicamente si tratta di una classe con uno scopo ben specifico.
Per esempio il file "logger.service.ts" può essere un servizio perchè contiene funzioni che hanno uno scopo ben preciso.
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
DEPENDENCY INJECTION Dependency injection è un modo per inizializzare una classe importando le dipendenze richieste. Per esempio per il componente (classe) HeroListComponent avrai bisogno di importare come dipendenza "HeroService".
Questo viene fatto nel seguente modo: constructor(private service: HeroService) { }
Perchè questo funzioni correttamente devi aver registrato "HeroService" come provider generale in "app.module.ts" in questo modo:
providers: [
BackendService,
HeroService,
Logger
],
oppure lo puoi registrare anche direttamente nella configurazione del componente:
@Component({
selector: 'hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})