Toate avantajele, fără bătăi de cap: un tutorial Angular 9

Publicat: 2022-03-11

„În fiecare an se întrerupe internetul”, se spune, iar dezvoltatorii trebuie de obicei să meargă să-l repare. Cu mult așteptata versiune Angular 9, s-ar putea crede că acest lucru s-ar aplica, iar aplicațiile dezvoltate pe versiuni anterioare ar trebui să treacă printr-un proces major de migrare.

Dar nu este cazul! Echipa Angular și-a reproiectat complet compilatorul, rezultând versiuni mai rapide, rulări de testare mai rapide, dimensiuni mai mici ale pachetelor și, cel mai important, compatibilitate cu versiunile mai vechi. Cu Angular 9, dezvoltatorii obțin practic toate avantajele fără nicio bătaie de cap.

În acest tutorial Angular 9, vom construi o aplicație Angular de la zero. Vom folosi unele dintre cele mai recente funcții Angular 9 și vom trece peste alte îmbunătățiri pe parcurs.

Tutorial Angular 9: Începând cu o nouă aplicație Angular

Să începem cu exemplul nostru de proiect Angular. Mai întâi, să instalăm cea mai recentă versiune a CLI-ului Angular:

 npm install -g @angular/cli

Putem verifica versiunea Angular CLI rulând ng version .

În continuare, să creăm o aplicație Angular:

 ng new ng9-app --create-application=false --strict

Folosim două argumente în ng new :

  • --create-application=false va spune CLI să genereze numai fișiere de spațiu de lucru. Acest lucru ne va ajuta să ne organizăm mai bine codul atunci când trebuie să avem mai multe aplicații și mai multe biblioteci.
  • --strict va adăuga reguli mai stricte pentru a impune mai multă tastare TypeScript și curățarea codului.

Drept urmare, avem un folder și fișiere de bază pentru spațiul de lucru.

O captură de ecran a unui IDE care arată folderul ng9-app, care conține node_modules, .editorconfig, .gitignore, angular.json, package-lock.json, package.json, README.md, tsconfig.json și tslint.json.

Acum, să adăugăm o nouă aplicație. Pentru a face asta, vom rula:

 ng generate application tv-show-rating

Ni se va solicita:

 ? 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

Acum, dacă rulăm ng serve , vom vedea aplicația rulând cu schelele sale inițiale.

O captură de ecran a schelei lui Angular 9, cu o notificare că „aplicația de evaluare a emisiunilor TV rulează!” Există, de asemenea, link-uri către resurse și pașii următori.

Dacă rulăm ng build --prod , putem vedea lista fișierelor generate.

O captură de ecran a ieșirii „ng build --prod” de la Angular 9. Începe cu „Generarea pachetelor ES5 pentru încărcare diferențială...” După ce s-a terminat, listează mai multe bucăți de fișiere JavaScript — runtime, polyfills și main, fiecare cu o versiune -es2015 și -es5 — și un fișier CSS. Linia finală oferă un marcaj de timp, hash și o durată de rulare de 23.881 milisecunde.

Avem două versiuni ale fiecărui fișier. Unul este compatibil cu browserele vechi, iar celălalt este compilat pentru ES2015, care utilizează API-uri mai noi și necesită mai puține polifillări pentru a rula pe browsere.

O mare îmbunătățire a Angular 9 este dimensiunea pachetului. Potrivit echipei Angular, puteți observa o scădere de până la 40% pentru aplicațiile mari.

Pentru o aplicație nou creată, dimensiunea pachetului este destul de similară cu cea a Angular 8, dar pe măsură ce aplicația dvs. crește, veți vedea că dimensiunea pachetului devine mai mică față de versiunile anterioare.

O altă caracteristică introdusă în Angular 9 este capacitatea de a ne avertiza dacă oricare dintre fișierele CSS de stil componente este mai mare decât un prag definit.

O captură de ecran a secțiunii „bugete” a unui fișier de configurare JSON Angular 9, cu două obiecte într-o matrice. Primul obiect are „type” setat la „inițial”, „maximumWarning” setat la „2mb” și „maximumError” setat la „5mb”. Al doilea obiect are „type” setat la „anyComponentStyle”, „maximumWarning” setat la „6kb” și „maximumError” setat la „10kb”.

Acest lucru ne va ajuta să prindem importurile de stil prost sau fișierele de stil de componente uriașe.

Adăugarea unui formular pentru a evalua emisiunile TV

În continuare, vom adăuga un formular pentru a evalua emisiunile TV. Pentru asta, mai întâi, vom instala bootstrap și ng-bootstrap :

 npm install bootstrap @ng-bootstrap/ng-bootstrap

O altă îmbunătățire a Angular 9 este i18n (internaționalizare). Anterior, dezvoltatorii trebuiau să ruleze o versiune completă pentru fiecare locație dintr-o aplicație. În schimb, Angular 9 ne permite să construim o aplicație o dată și să generăm toate fișierele i18n într-un proces post-build, reducând semnificativ timpul de construire. Deoarece ng-bootstrap are o dependență de i18n, vom adăuga noul pachet la proiectul nostru:

 ng add @angular/localize

Apoi, vom adăuga tema Bootstrap la styles.scss al aplicației noastre :

 @import "~bootstrap/scss/bootstrap";

Și vom include NgbModule și ReactiveFormsModule în AppModule pe app.module.ts :

 // ... import { ReactiveFormsModule } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ imports: [ // ... ReactiveFormsModule, NgbModule ], })

În continuare, vom actualiza app.component.html cu o grilă de bază pentru formularul nostru:

 <div class="container"> <div class="row"> <div class="col-6"> </div> </div> </div>

Și generați componenta formularului:

 ng gc TvRatingForm

Să actualizăm tv-rating-form.component.html și să adăugăm formularul pentru a evalua emisiunile 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>

Și tv-rating-form.component.ts va arăta astfel:

 // ... 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(); } }

În cele din urmă, să adăugăm formularul la app.component.html :

 <!-- ... --> <div class="col-6"> <app-tv-rating-form></app-tv-rating-form> </div>

În acest moment, avem câteva funcționalități de bază ale UI. Acum, dacă rulăm ng serve din nou, îl putem vedea în acțiune.

O captură de ecran a unei aplicații tutorial Angular 9 care arată un formular intitulat „TV SHOW”, cu un meniu vertical care listează câteva titluri de emisiuni, un indicator de stele și un buton OK. În animație, utilizatorul selectează o emisiune, selectează o evaluare și apoi dă clic pe butonul OK.

Înainte de a trece mai departe, haideți să aruncăm o privire rapidă asupra unor noi funcții interesante Angular 9 care au fost adăugate pentru a ajuta la depanare. Deoarece aceasta este o sarcină foarte comună în munca noastră zilnică, merită să știm ce s-a schimbat pentru a ne ușura puțin viața.

Depanare cu Angular 9 Ivy

O altă îmbunătățire majoră introdusă în Angular 9 și Angular Ivy este experiența de depanare. Acum compilatorul poate detecta mai multe erori și le poate arunca într-un mod mai „lizibil”.

Să-l vedem în acțiune. Mai întâi, vom activa verificarea șablonului în tsconfig.json :

 { // ... "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "strictTemplates": true } }

Acum, dacă actualizăm matricea tvShows și redenumim name în title :

 tvShows = [ { title: 'Better call Saul!' }, { title: 'Breaking Bad' }, { title: 'Lost' }, { title: 'Mad men' } ];

… vom primi o eroare de la compilator.

O captură de ecran a ieșirii compilatorului Angular 9/Angular Ivy, cu un nume de fișier și o poziție, care spune „eroare TS2339: Proprietatea „nume” nu există pe tipul „{ titlu: șir; }””. De asemenea, arată linia de cod în cauză și subliniază referința, în acest caz în fișierul tv-rating-form.component.html unde este menționat tvShow.name. După aceea, referința la acest fișier HTML este urmărită la fișierul TypeScript corespunzător și evidențiată în mod similar.

Această verificare de tip ne va permite să prevenim greșelile de scriere și utilizarea incorectă a tipurilor TypeScript.

Validare Angular Ivy pentru @Input()

O altă validare bună pe care o primim este cu @Input() . De exemplu, am putea adăuga acest lucru la tv-rating-form.component.ts :

 @Input() title: string;

...și legați-l în app.component.html :

 <app-tv-rating-form [title]="title"></app-tv-rating-form>

… și apoi schimbați app.component.ts astfel:

 // ... export class AppComponent { title = null; }

Dacă facem aceste trei modificări, vom primi un alt tip de eroare de la compilator.

O captură de ecran a ieșirii compilatorului Angular 9/Angular Ivy, într-un format similar cu cel precedent, evidențiind app.component.html cu „eroare TS 2322: Tipul „null” nu poate fi atribuit tipului „șir””.

În cazul în care dorim să-l ocolim, putem folosi $any() pe șablon pentru a arunca valoarea în any și a remedia eroarea:

 <app-tv-rating-form [title]="$any(title)"></app-tv-rating-form>

Modul corect de a remedia acest lucru, totuși, ar fi să faceți ca title din formular să nu fie nul:

 @Input() title: string | null ;

The ExpressionChangedAfterItHasBeenCheckedError în Angular 9 Ivy

Una dintre cele mai de temut erori în dezvoltarea Angular este ExpressionChangedAfterItHasBeenCheckedError . Din fericire, Ivy afișează eroarea într-un mod mai clar, făcând mai ușor de găsit de unde vine problema.

Deci, să introducem o eroare ExpressionChangedAfterItHasBeenCheckedError . Pentru a face asta, mai întâi, vom genera un serviciu:

 ng gs Title

Apoi, vom adăuga un BehaviorSubject și metode pentru a accesa Observable și pentru a emite o nouă valoare.

 export class TitleService { private bs = new BehaviorSubject < string > (''); constructor() {} get title$() { return this.bs.asObservable(); } update(title: string) { this.bs.next(title); } }

După aceea, vom adăuga acest lucru la app.component.html :

 <!-- ... --> <div class="col-6"> <h2> {{title$ | async}} </h2> <app-tv-rating-form [title]="title"></app-tv-rating-form> </div>

Și în app.component.ts , vom injecta TitleService :

 export class AppComponent implements OnInit { // ... title$: Observable < string > ; constructor( private titleSvc: TitleService ) {} ngOnInit() { this.title$ = this.titleSvc.title$; } // ... }

În cele din urmă, în tv-rating-form.component.ts , vom injecta TitleService și vom actualiza titlul AppComponent , care va genera o eroare ExpressionChangedAfterItHasBeenCheckedError .

 // ... constructor( private titleSvc: TitleService ) { } ngOnInit() { this.titleSvc.update('new title!'); }

Acum putem vedea eroarea detaliată în consola de dezvoltare a browserului, iar făcând clic pe app.component.html ne va indica unde este eroarea.

O captură de ecran a consolei de dezvoltare a browserului, care arată raportarea de către Angular Ivy a erorii ExpressionChangedAfterItHasBeenCheckedError. O urmă de stivă în text roșu oferă eroarea, împreună cu valorile anterioare și actuale, și un indiciu. În mijlocul urmăririi stivei este singura linie care nu se referă la core.js. Utilizatorul face clic pe el și este adus la linia app.component.html care cauzează eroarea.

Putem remedia această eroare încheind apelul de service cu setTimeout :

 setTimeout(() => { this.titleSvc.update('new title!'); });

Pentru a înțelege de ce se întâmplă eroarea ExpressionChangedAfterItHasBeenCheckedError și pentru a explora alte posibilități, merită citită postarea lui Maxim Koretskyi pe acest subiect.

Angular Ivy ne permite să avem erorile prezentate într-un mod mai clar și ajută la aplicarea tastării TypeScript în codul nostru. În secțiunea următoare, vom acoperi câteva scenarii comune în care vom profita de Ivy și de depanare.

Scrierea unui test pentru aplicația noastră Angular 9 cu hamuri de componente

În Angular 9, a fost introdus un nou API de testare numit harnașuri de componente . Ideea din spatele acestuia este de a elimina toate sarcinile necesare pentru a interacționa cu DOM, făcându-l mult mai ușor de lucrat și mai stabil de rulat.

API-ul pentru cablarea componentelor este inclus în biblioteca @angular/cdk , așa că haideți să o instalăm mai întâi pe proiectul nostru:

 npm install @angular/cdk

Acum putem scrie un test și putem folosi hamurile de componente. În tv-rating-form.component.spec.ts , să setăm testul:

 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(); })); // ... });

Apoi, să implementăm un ComponentHarness pentru componenta noastră. Vom crea două hamuri: unul pentru TvRatingForm și altul pentru NgbRating . ComponentHarness necesită un câmp static , hostSelector , care ar trebui să ia valoarea selectorului componentei.

 // ... 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'; } // ...

Pentru TvRatingFormHarness , vom crea un selector pentru butonul de trimitere și o funcție pentru a declanșa un click . Puteți vedea cât de ușor devine implementarea acestui lucru.

 class TvRatingFormHarness extends ComponentHarness { // ... protected getButton = this.locatorFor('button'); async submit() { const button = await this.getButton(); await button.click(); } }

În continuare, vom adăuga metode pentru a seta o evaluare. Aici folosim locatorForAll pentru a căuta toate elementele <span> care reprezintă stelele pe care utilizatorul poate face clic. Funcția rate doar primește toate stelele evaluărilor posibile și dă clic pe cea corespunzătoare valorii trimise.

 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(); } }

Ultima piesă care lipsește este conectarea TvRatingFormHarness la NgbRatingHarness . Pentru a face asta, doar adăugăm locatorul în clasa TvRatingFormHarness .

 class TvRatingFormHarness extends ComponentHarness { // ... getRating = this.locatorFor(NgbRatingHarness); // ... }

Acum, să scriem testul nostru:

 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}'); }); });

Observați că pentru select noastră din formular, nu am implementat setarea valorii acesteia printr-un ham. Asta pentru că API-ul încă nu acceptă selectarea unei opțiuni. Dar acest lucru ne oferă șansa de a compara aici cum arăta interacțiunea cu elementele înainte de hamurile componente.

Un ultim lucru înainte de a rula testele. Trebuie să reparăm app.component.spec.ts , deoarece am actualizat title pentru a fi 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); }); });

Acum, când rulăm ng test , testul nostru trece.

O captură de ecran a testelor Karma care rulează în aplicația noastră Angular 9. Afișează „Ran 2 of 6 specs” cu mesajul „Incomplet: fit() or fdescribe() was found, 2 specs, 0 failures, randomized with seed 69573.” Cele două teste ale TvRatingFormComponent sunt evidențiate. Cele trei teste ale AppComponent și un test al TitleService sunt toate gri.

Înapoi la aplicația noastră de exemplu Angular 9: Salvarea datelor într-o bază de date

Să încheiem tutorialul nostru Angular 9 adăugând o conexiune la Firestore și salvând evaluările în baza de date.

Pentru a face asta, trebuie să creăm un proiect Firebase. Apoi, vom instala dependențele necesare.

 npm install @angular/fire firebase

În setările de proiect ale Consolei Firebase, vom obține configurația acesteia și le vom adăuga la environment.ts și 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}' } };

După aceea, vom importa modulele necesare în 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, ], // ... })

Apoi, în tv-rating-form.component.ts , vom injecta serviciul AngularFirestore și vom salva o nouă evaluare la trimiterea formularului:

 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(); } } 

O captură de ecran a unei aplicații tutorial Angular 9 care arată un formular intitulat „TV SHOW” sub titlul unei pagini mai mare „titlu nou!” Din nou, are o listă derulantă care listează câteva titluri de emisiuni, un indicator de stele și un buton OK, iar utilizatorul selectează o emisiune, selectează o evaluare și apoi dă clic pe butonul OK.

Acum, când mergem la Consola Firebase, vom vedea elementul nou creat.

O captură de ecran a consolei Firebase. În coloana din stânga este joaq-lab cu câteva colecții: participanți, curse, evaluări, testare și utilizatori. Elementul de evaluare este selectat și este prezentat în coloana din mijloc cu un ID selectat - este singurul document. Coloana din dreapta afișează două câmpuri: „evaluare” este setată la 4, iar „tvShow” este setată la „Oameni nebuni”.

În cele din urmă, să listăm toate evaluările în AppComponent . Pentru a face asta, în app.component.ts , vom obține datele din colecție:

 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(); } }

… și în app.component.html , vom adăuga o listă de evaluări:

 <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>

Așa arată aplicația noastră tutorial Angular 9 atunci când este pusă la cap.

O captură de ecran a unei aplicații tutorial Angular 9 care arată un formular intitulat „TV SHOW” sub titlul unei pagini mai mare „titlu nou!” Din nou, are un meniu derulant care listează o mână de titluri de emisiuni, un contor de stele și un buton OK. De data aceasta, o coloană din dreapta listează deja „Mad men (4)”, iar utilizatorul evaluează Lost la trei stele, urmat de „Mad men” din nou la patru stele. Coloana din dreapta rămâne ordonată alfabetic după ambele evaluări noi.

Angular 9 și Angular Ivy: dezvoltare mai bună, aplicații mai bune și compatibilitate mai bună

În acest tutorial Angular 9, am abordat construirea unui formular de bază, salvarea datelor în Firebase și preluarea elementelor din acesta.

Pe parcurs, am văzut ce îmbunătățiri și funcții noi sunt incluse pe Angular 9 și Angular Ivy. Pentru o listă completă, puteți verifica cea mai recentă postare de lansare a blogului oficial Angular.


Insigna Google Cloud Partner.

În calitate de partener Google Cloud, experții Toptal certificați de Google sunt disponibili companiilor la cerere pentru cele mai importante proiecte ale acestora.