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=falseva 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. -
--strictva 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.
Acum, să adăugăm o nouă aplicație. Pentru a face asta, vom rula:
ng generate application tv-show-ratingNi 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.
Dacă rulăm ng build --prod , putem vedea lista fișierelor generate.
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.
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.
Î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.
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.
Î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.
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.
Î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(); } } Acum, când mergem la Consola Firebase, vom vedea elementul nou creat.
Î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.
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.
Î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.
