Tutti i vantaggi, nessun problema: un tutorial di Angular 9
Pubblicato: 2022-03-11"Ogni anno Internet si interrompe", dice il proverbio, e gli sviluppatori di solito devono andare a sistemarlo. Con la tanto attesa versione 9 di Angular, si potrebbe pensare che ciò si applichi e le app sviluppate su versioni precedenti dovrebbero passare attraverso un importante processo di migrazione.
Ma non è così! Il team di Angular ha completamente riprogettato il suo compilatore, ottenendo build più veloci, esecuzioni di test più veloci, dimensioni dei bundle più piccole e, soprattutto, compatibilità con le versioni precedenti. Con Angular 9, gli sviluppatori ottengono praticamente tutti i vantaggi senza problemi.
In questo tutorial di Angular 9, creeremo un'applicazione Angular da zero. Utilizzeremo alcune delle ultime funzionalità di Angular 9 e esamineremo altri miglioramenti lungo il percorso.
Tutorial Angular 9: iniziare con una nuova applicazione angolare
Iniziamo con il nostro esempio di progetto Angular. Innanzitutto, installiamo l'ultima versione della CLI di Angular:
npm install -g @angular/cli
Possiamo verificare la versione Angular CLI eseguendo ng version
.
Quindi, creiamo un'applicazione Angular:
ng new ng9-app --create-application=false --strict
Stiamo usando due argomenti nel nostro ng new
:
-
--create-application=false
indicherà alla CLI di generare solo file dell'area di lavoro. Questo ci aiuterà a organizzare meglio il nostro codice quando avremo bisogno di più di un'app e più librerie. -
--strict
aggiungerà regole più rigorose per imporre più digitazione TypeScript e pulizia del codice.
Di conseguenza, abbiamo una cartella e file dell'area di lavoro di base.
Ora aggiungiamo una nuova app. Per farlo, eseguiremo:
ng generate application tv-show-rating
Ci verrà richiesto:
? Would you like to share anonymous usage data about this project with the Angular Team at Google under Google's Privacy Policy at https://policies.google.com/privacy? For more details and how to change this setting, see http://angular.io/analytics. No ? Would you like to add Angular routing? Yes ? Which stylesheet format would you like to use? SCSS
Ora, se eseguiamo ng serve
, vedremo l'app in esecuzione con il suo scaffolding iniziale.
Se eseguiamo ng build --prod
, possiamo vedere l'elenco dei file generati.
Abbiamo due versioni di ogni file. Uno è compatibile con i browser legacy e l'altro è compilato per ES2015, che utilizza API più recenti e richiede meno polyfill per essere eseguito sui browser.
Un grande miglioramento di Angular 9 è la dimensione del pacchetto. Secondo il team di Angular, puoi vedere una diminuzione fino al 40% per le grandi app.
Per un'app appena creata, la dimensione del pacchetto è abbastanza simile a quella di Angular 8, ma man mano che la tua app cresce, vedrai la dimensione del pacchetto diventare più piccola rispetto alle versioni precedenti.
Un'altra funzionalità introdotta in Angular 9 è la possibilità di avvisarci se uno qualsiasi dei file CSS in stile componente è più grande di una soglia definita.
Questo ci aiuterà a rilevare le importazioni di stile errato o file di stile di componenti di grandi dimensioni.
Aggiunta di un modulo per valutare i programmi TV
Successivamente, aggiungeremo un modulo per valutare i programmi TV. Per questo, per prima cosa, installeremo bootstrap
e ng-bootstrap
:
npm install bootstrap @ng-bootstrap/ng-bootstrap
Un altro miglioramento su Angular 9 è i18n (internazionalizzazione). In precedenza, gli sviluppatori avrebbero dovuto eseguire una build completa per ogni locale in un'app. Angular 9 invece ci consente di creare un'app una volta e generare tutti i file i18n in un processo di post-compilazione, riducendo significativamente i tempi di costruzione. Poiché ng-bootstrap
ha una dipendenza da i18n, aggiungeremo il nuovo pacchetto al nostro progetto:
ng add @angular/localize
Successivamente, aggiungeremo il tema Bootstrap al styles.scss
della nostra app:
@import "~bootstrap/scss/bootstrap";
E includeremo NgbModule
e ReactiveFormsModule
nel nostro AppModule
su app.module.ts
:
// ... import { ReactiveFormsModule } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ imports: [ // ... ReactiveFormsModule, NgbModule ], })
Successivamente, aggiorneremo app.component.html
con una griglia di base per il nostro modulo:
<div class="container"> <div class="row"> <div class="col-6"> </div> </div> </div>
E genera il componente del modulo:
ng gc TvRatingForm
Aggiorniamo tv-rating-form.component.html
e aggiungiamo il modulo per valutare i programmi TV.
<form [formGroup]="form" (ngSubmit)="submit()" class="mt-3"> <div class="form-group"> <label>TV SHOW</label> <select class="custom-select" formControlName="tvShow"> <option *ngFor="let tvShow of tvShows" [value]="tvShow.name">{{tvShow.name}}</option> </select> </div> <div class="form-group"> <ngb-rating [max]="5" formControlName="rating"></ngb-rating> </div> <button [disabled]="form.invalid || form.disabled" class="btn btn-primary">OK</button> </form>
E tv-rating-form.component.ts
apparirà così:
// ... export class TvRatingFormComponent implements OnInit { tvShows = [ { name: 'Better call Saul!' }, { name: 'Breaking Bad' }, { name: 'Lost' }, { name: 'Mad men' } ]; form = new FormGroup({ tvShow: new FormControl('', Validators.required), rating: new FormControl('', Validators.required), }); submit() { alert(JSON.stringify(this.form.value)); this.form.reset(); } }
Infine, aggiungiamo il modulo a app.component.html
:
<!-- ... --> <div class="col-6"> <app-tv-rating-form></app-tv-rating-form> </div>
A questo punto, abbiamo alcune funzionalità di base dell'interfaccia utente. Ora, se eseguiamo di nuovo ng serve
, possiamo vederlo in azione.
Prima di andare avanti, diamo una rapida occhiata ad alcune nuove interessanti funzionalità di Angular 9 che sono state aggiunte per aiutare il debug. Poiché questo è un compito molto comune nel nostro lavoro quotidiano, vale la pena sapere cosa è cambiato per rendere la nostra vita un po' più facile.
Debug con Angular 9 Ivy
Un altro grande miglioramento introdotto in Angular 9 e Angular Ivy è l'esperienza di debug. Il compilatore ora può rilevare più errori e lanciarli in un modo più "leggibile".
Vediamolo in azione. Innanzitutto, attiveremo il controllo del modello in tsconfig.json
:
{ // ... "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "strictTemplates": true } }
Ora, se aggiorniamo l'array tvShows
e rinominiamo il name
in title
:
tvShows = [ { title: 'Better call Saul!' }, { title: 'Breaking Bad' }, { title: 'Lost' }, { title: 'Mad men' } ];
... riceveremo un errore dal compilatore.
Questo controllo del tipo ci consentirà di prevenire errori di battitura e l'utilizzo errato dei tipi TypeScript.
Convalida Ivy angolare per @Input()
Un'altra buona convalida che otteniamo è con @Input()
. Ad esempio, potremmo aggiungere questo a tv-rating-form.component.ts
:
@Input() title: string;
...e collegalo in app.component.html
:
<app-tv-rating-form [title]="title"></app-tv-rating-form>
... e poi cambia app.component.ts
in questo modo:
// ... export class AppComponent { title = null; }
Se apportiamo queste tre modifiche, otterremo un altro tipo di errore dal compilatore.
Nel caso in cui desideriamo bypassarlo, possiamo utilizzare $any()
sul modello per eseguire il cast del valore su any
e correggere l'errore:
<app-tv-rating-form [title]="$any(title)"></app-tv-rating-form>
Il modo giusto per risolvere questo problema, tuttavia, sarebbe rendere il title
sul modulo annullabile:
@Input() title: string | null ;
The ExpressionChangedAfterItHasBeenCheckedError
in Angular 9 Ivy
Uno degli errori più temuti nello sviluppo di Angular è ExpressionChangedAfterItHasBeenCheckedError
. Per fortuna, Ivy restituisce l'errore in modo più chiaro, rendendo più facile trovare l'origine del problema.
Quindi, introduciamo un errore ExpressionChangedAfterItHasBeenCheckedError
. Per fare ciò, per prima cosa, genereremo un servizio:
ng gs Title
Successivamente, aggiungeremo un BehaviorSubject
e metodi per accedere a Observable
ed emettere un nuovo valore.
export class TitleService { private bs = new BehaviorSubject < string > (''); constructor() {} get title$() { return this.bs.asObservable(); } update(title: string) { this.bs.next(title); } }
Successivamente, lo aggiungeremo a app.component.html
:

<!-- ... --> <div class="col-6"> <h2> {{title$ | async}} </h2> <app-tv-rating-form [title]="title"></app-tv-rating-form> </div>
E in app.component.ts
, inietteremo il TitleService
:
export class AppComponent implements OnInit { // ... title$: Observable < string > ; constructor( private titleSvc: TitleService ) {} ngOnInit() { this.title$ = this.titleSvc.title$; } // ... }
Infine, in tv-rating-form.component.ts
, inietteremo TitleService
e aggiorneremo il titolo di AppComponent
, che genererà un errore ExpressionChangedAfterItHasBeenCheckedError
.
// ... constructor( private titleSvc: TitleService ) { } ngOnInit() { this.titleSvc.update('new title!'); }
Ora possiamo vedere l'errore dettagliato nella console di sviluppo del browser e facendo clic su app.component.html
ci indicherà dove si trova l'errore.
Possiamo correggere questo errore avvolgendo la chiamata di servizio con setTimeout
:
setTimeout(() => { this.titleSvc.update('new title!'); });
Per capire perché si verifica l'errore ExpressionChangedAfterItHasBeenCheckedError
ed esplorare altre possibilità, vale la pena leggere il post di Maxim Koretskyi sull'argomento.
Angular Ivy ci consente di presentare gli errori in modo più chiaro e aiuta a imporre la digitazione di TypeScript nel nostro codice. Nella sezione seguente, tratteremo alcuni scenari comuni in cui sfrutteremo Ivy e il debug.
Scrivere un test per la nostra app Angular 9 con cablaggi di componenti
In Angular 9 è stata introdotta una nuova API di test denominata cablaggio dei componenti . L'idea alla base è quella di rimuovere tutto il lavoro necessario per interagire con il DOM, rendendo molto più facile lavorare e più stabile da eseguire.
L'API del cablaggio dei componenti è inclusa nella libreria @angular/cdk
, quindi installiamola prima sul nostro progetto:
npm install @angular/cdk
Ora possiamo scrivere un test e sfruttare i cablaggi dei componenti. In tv-rating-form.component.spec.ts
, impostiamo il test:
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ReactiveFormsModule } from '@angular/forms'; describe('TvRatingFormComponent', () => { let component: TvRatingFormComponent; let fixture: ComponentFixture < TvRatingFormComponent > ; beforeEach(async (() => { TestBed.configureTestingModule({ imports: [ NgbModule, ReactiveFormsModule ], declarations: [TvRatingFormComponent] }).compileComponents(); })); // ... });
Quindi, implementiamo un ComponentHarness
per il nostro componente. Creeremo due cablaggi: uno per TvRatingForm
e un altro per NgbRating
. ComponentHarness
richiede un campo static
, hostSelector
, che dovrebbe assumere il valore del selettore del componente.
// ... import { ComponentHarness, HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; class TvRatingFormHarness extends ComponentHarness { static hostSelector = 'app-tv-rating-form'; } class NgbRatingHarness extends ComponentHarness { static hostSelector = 'ngb-rating'; } // ...
Per il nostro TvRatingFormHarness
, creeremo un selettore per il pulsante di invio e una funzione per attivare un click
. Puoi vedere quanto diventa più facile implementarlo.
class TvRatingFormHarness extends ComponentHarness { // ... protected getButton = this.locatorFor('button'); async submit() { const button = await this.getButton(); await button.click(); } }
Successivamente, aggiungeremo metodi per impostare una valutazione. Qui utilizziamo locatorForAll
per cercare tutti gli elementi <span>
che rappresentano le stelle su cui l'utente può fare clic. La funzione di rate
ottiene solo tutte le possibili stelle di valutazione e fa clic su quella corrispondente al valore inviato.
class NgbRatingHarness extends ComponentHarness { // ... protected getRatings = this.locatorForAll('span:not(.sr-only)'); async rate(value: number) { const ratings = await this.getRatings(); return ratings[value - 1].click(); } }
L'ultimo pezzo mancante è collegare TvRatingFormHarness
a NgbRatingHarness
. Per farlo, aggiungiamo semplicemente il localizzatore sulla classe TvRatingFormHarness
.
class TvRatingFormHarness extends ComponentHarness { // ... getRating = this.locatorFor(NgbRatingHarness); // ... }
Ora scriviamo il nostro test:
describe('TvRatingFormComponent', () => { // ... it('should pop an alert on submit', async () => { spyOn(window, 'alert'); const select = fixture.debugElement.query(By.css('select')).nativeElement; select.value = 'Lost'; select.dispatchEvent(new Event('change')); fixture.detectChanges(); const harness = await TestbedHarnessEnvironment.harnessForFixture(fixture, TvRatingFormHarness); const rating = await harness.getRating(); await rating.rate(1); await harness.submit(); expect(window.alert).toHaveBeenCalledWith('{"tvShow":"Lost","rating":1}'); }); });
Si noti che per la nostra select
all'interno del modulo, non abbiamo implementato l'impostazione del suo valore tramite un'imbracatura. Questo perché l'API non supporta ancora la selezione di un'opzione. Ma questo ci dà la possibilità di confrontare qui come appariva l'interazione con gli elementi prima dei cablaggi dei componenti.
Un'ultima cosa prima di eseguire i test. Dobbiamo correggere app.component.spec.ts
poiché abbiamo aggiornato il title
in modo che sia null
.
describe('AppComponent', () => { // ... it(`should have as title 'tv-show-rating'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual(null); }); });
Ora, quando eseguiamo ng test
, il nostro test passa.
Torna alla nostra app di esempio Angular 9: salvataggio dei dati in un database
Concludiamo il nostro tutorial su Angular 9 aggiungendo una connessione a Firestore e salvando le valutazioni nel database.
Per farlo, dobbiamo creare un progetto Firebase. Quindi, installeremo le dipendenze richieste.
npm install @angular/fire firebase
Nelle impostazioni del progetto di Firebase Console, otterremo la sua configurazione e le aggiungeremo a environment.ts
e environment.prod.ts
:
export const environment = { // ... firebase: { apiKey: '{your-api-key}', authDomain: '{your-project-id}.firebaseapp.com', databaseURL: 'https://{your-project-id}.firebaseio.com', projectId: '{your-project-id}', storageBucket: '{your-project-id}.appspot.com', messagingSenderId: '{your-messaging-id}', appId: '{your-app-id}' } };
Successivamente, importeremo i moduli necessari in app.module.ts
:
import { AngularFireModule } from '@angular/fire'; import { AngularFirestoreModule } from '@angular/fire/firestore'; import { environment } from '../environments/environment'; @NgModule({ // ... imports: [ // ... AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule, ], // ... })
Successivamente, in tv-rating-form.component.ts
, inietteremo il servizio AngularFirestore
e salveremo una nuova valutazione all'invio del modulo:
import { AngularFirestore } from '@angular/fire/firestore'; export class TvRatingFormComponent implements OnInit { constructor( // ... private af: AngularFirestore, ) { } async submit(event: any) { this.form.disable(); await this.af.collection('ratings').add(this.form.value); this.form.enable(); this.form.reset(); } }
Ora, quando andiamo alla Firebase Console, vedremo l'elemento appena creato.
Infine, elenchiamo tutte le valutazioni in AppComponent
. Per fare ciò, in app.component.ts
otterremo i dati dalla raccolta:
import { AngularFirestore } from '@angular/fire/firestore'; export class AppComponent implements OnInit { // ... ratings$: Observable<any>; constructor( // ... private af: AngularFirestore ) { } ngOnInit() { // ... this.ratings$ = this.af.collection('ratings').valueChanges(); } }
…e in app.component.html
aggiungeremo un elenco di valutazioni:
<div class="container"> <div class="row"> // ... <div class="col-6"> <div> <p *ngFor="let rating of ratings$ | async"> {{rating.tvShow}} ({{rating.rating}}) </p> </div> </div> </div> </div>
Ecco come appare la nostra app tutorial Angular 9 quando è tutto messo insieme.
Angular 9 e Angular Ivy: migliore sviluppo, migliori app e migliore compatibilità
In questo tutorial di Angular 9, abbiamo trattato la creazione di un modulo di base, il salvataggio dei dati su Firebase e il recupero di elementi da esso.
Lungo la strada, abbiamo visto quali miglioramenti e nuove funzionalità sono inclusi in Angular 9 e Angular Ivy. Per un elenco completo, puoi controllare l'ultimo post di rilascio del blog ufficiale di Angular.
In qualità di Google Cloud Partner, gli esperti certificati da Google di Toptal sono a disposizione delle aziende on demand per i loro progetti più importanti.