جميع الامتيازات ، بلا متاعب: درس 9 Angular
نشرت: 2022-03-11يقول المثل "كل عام ينقطع الإنترنت" ، وعادة ما يتعين على المطورين الذهاب وإصلاحه. مع الإصدار 9 من Angular الذي طال انتظاره ، قد يعتقد المرء أن هذا ينطبق ، وأن التطبيقات التي تم تطويرها على الإصدارات السابقة ستحتاج إلى المرور بعملية ترحيل رئيسية.
لكن ليست هذه هي المسألة! أعاد فريق Angular تصميم مترجمه بالكامل ، مما أدى إلى إنشاءات أسرع ، وتشغيل اختبار أسرع ، وحجم حزم أصغر ، والأهم من ذلك ، التوافق مع الإصدارات السابقة مع الإصدارات القديمة. مع Angular 9 ، يحصل المطورون بشكل أساسي على جميع الامتيازات دون أي متاعب.
في هذا البرنامج التعليمي Angular 9 ، سننشئ تطبيق Angular من البداية. سنستخدم بعضًا من أحدث ميزات Angular 9 ونراجع التحسينات الأخرى على طول الطريق.
Angular 9 Tutorial: البدء بتطبيق Angular جديد
لنبدأ في مثال مشروع Angular الخاص بنا. أولاً ، دعنا نثبت أحدث إصدار من Angular's CLI:
npm install -g @angular/cli
يمكننا التحقق من إصدار Angular CLI بتشغيل ng version
.
بعد ذلك ، دعنا ننشئ تطبيق Angular:
ng new ng9-app --create-application=false --strict
نحن نستخدم وسيطتين في أمرنا ng new
:
-
--create-application=false
سيخبر CLI بإنشاء ملفات مساحة العمل فقط. سيساعدنا هذا في تنظيم الكود الخاص بنا بشكل أفضل عندما نحتاج إلى أكثر من تطبيق واحد ومكتبات متعددة. - - ستضيف صارمة قواعد أكثر صرامة لفرض المزيد من كتابة
--strict
ونظافة التعليمات البرمجية.
نتيجة لذلك ، لدينا مجلد وملفات مساحة عمل أساسية.
الآن ، دعنا نضيف تطبيقًا جديدًا. للقيام بذلك ، سنقوم بتشغيل:
ng generate application tv-show-rating
سيُطلب منا:
? 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
الآن ، إذا قمنا بتشغيل ng serve
، فسنرى التطبيق يعمل بسقالاته الأولية.
إذا قمنا بتشغيل ng build --prod
، فيمكننا رؤية قائمة الملفات التي تم إنشاؤها.
لدينا نسختان من كل ملف. أحدهما متوافق مع المتصفحات القديمة ، والآخر تم تجميعه لاستهداف ES2015 ، والذي يستخدم واجهات برمجة تطبيقات أحدث ويتطلب عددًا أقل من polyfill للتشغيل على المتصفحات.
أحد التحسينات الكبيرة في Angular 9 هو حجم الحزمة. وفقًا لفريق Angular ، يمكنك أن ترى انخفاضًا يصل إلى 40٪ في التطبيقات الكبيرة.
بالنسبة إلى تطبيق تم إنشاؤه حديثًا ، فإن حجم الحزمة يشبه إلى حد كبير حجم Angular 8 ، ولكن مع نمو تطبيقك ، ستلاحظ أن حجم الحزمة أصبح أصغر مقارنة بالإصدارات السابقة.
ميزة أخرى تم تقديمها في Angular 9 هي القدرة على تحذيرنا إذا كان أي من ملفات CSS ذات النمط المكون أكبر من عتبة محددة.
سيساعدنا هذا في التعرف على عمليات الاستيراد ذات الأنماط السيئة أو ملفات نمط المكونات الضخمة.
إضافة نموذج لتقييم العروض التليفزيونية
بعد ذلك ، سنضيف نموذجًا لتقييم البرامج التلفزيونية. لذلك ، سنقوم أولاً بتثبيت bootstrap
و ng-bootstrap
:
npm install bootstrap @ng-bootstrap/ng-bootstrap
تحسين آخر على Angular 9 هو i18n (التدويل). في السابق ، كان المطورون بحاجة إلى تشغيل تصميم كامل لكل لغة في التطبيق. بدلاً من ذلك ، يتيح لنا Angular 9 إنشاء تطبيق مرة واحدة وإنشاء جميع ملفات i18n في عملية ما بعد الإنشاء ، مما يقلل بشكل كبير من وقت الإنشاء. نظرًا لأن ng-bootstrap
يعتمد على i18n ، فسنضيف الحزمة الجديدة إلى مشروعنا:
ng add @angular/localize
بعد ذلك ، سنضيف سمة Bootstrap إلى styles.scss
التطبيق الخاص بنا:
@import "~bootstrap/scss/bootstrap";
وسنقوم بتضمين NgbModule
و ReactiveFormsModule
في AppModule
الخاص بنا على app.module.ts
:
// ... import { ReactiveFormsModule } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ imports: [ // ... ReactiveFormsModule, NgbModule ], })
بعد ذلك ، سنقوم بتحديث app.component.html
بشبكة أساسية لنموذجنا:
<div class="container"> <div class="row"> <div class="col-6"> </div> </div> </div>
وإنشاء مكون النموذج:
ng gc TvRatingForm
لنقوم بتحديث tv-rating-form.component.html
وإضافة النموذج لتقييم البرامج التلفزيونية.
<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>
tv-rating-form.component.ts
بالشكل التالي:
// ... 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(); } }
أخيرًا ، دعنا نضيف النموذج إلى app.component.html
:
<!-- ... --> <div class="col-6"> <app-tv-rating-form></app-tv-rating-form> </div>
في هذه المرحلة ، لدينا بعض وظائف واجهة المستخدم الأساسية. الآن ، إذا قمنا بتشغيل ng serve
مرة أخرى ، فيمكننا رؤيتها أثناء العمل.
قبل المضي قدمًا ، دعنا نلقي نظرة سريعة على بعض ميزات Angular 9 الجديدة والمثيرة للاهتمام والتي تمت إضافتها للمساعدة في تصحيح الأخطاء. نظرًا لأن هذه مهمة شائعة جدًا في عملنا اليومي ، فمن الجدير معرفة ما الذي تغير لجعل حياتنا أسهل قليلاً.
التصحيح باستخدام Angular 9 Ivy
تحسين كبير آخر تم تقديمه في Angular 9 و Angular Ivy هو تجربة التصحيح. يمكن للمجمع الآن اكتشاف المزيد من الأخطاء ورميها بطريقة "مقروءة" بشكل أكبر.
دعونا نراه في العمل. أولاً ، سنقوم بتنشيط فحص النموذج في tsconfig.json
:
{ // ... "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "strictTemplates": true } }
الآن ، إذا قمنا بتحديث مصفوفة tvShows
تسمية name
title
:
tvShows = [ { title: 'Better call Saul!' }, { title: 'Breaking Bad' }, { title: 'Lost' }, { title: 'Mad men' } ];
... سنحصل على خطأ من المترجم.
سيسمح لنا فحص النوع هذا بمنع الأخطاء الإملائية والاستخدام غير الصحيح لأنواع TypeScript.
التحقق من صحة اللبلاب الزاوي لـ @Input()
التحقق الجيد الآخر الذي نحصل عليه هو باستخدام @Input()
. على سبيل المثال ، قد نضيف هذا إلى tv-rating-form.component.ts
:
@Input() title: string;
… وربطها في app.component.html
:
<app-tv-rating-form [title]="title"></app-tv-rating-form>
... ثم قم بتغيير app.component.ts
مثل:
// ... export class AppComponent { title = null; }
إذا قمنا بإجراء هذه التغييرات الثلاثة ، فسنحصل على نوع آخر من الخطأ من المترجم.
في حال أردنا تجاوزه ، يمكننا استخدام $any()
في القالب لإعطاء القيمة any
منها وإصلاح الخطأ:
<app-tv-rating-form [title]="$any(title)"></app-tv-rating-form>
الطريقة الصحيحة لإصلاح هذا ، على الرغم من ذلك ، ستكون جعل title
في النموذج لاغياً:
@Input() title: string | null ;
The ExpressionChangedAfterItHasBeenCheckedError
في Angular 9 Ivy
يعد خطأ ExpressionChangedAfterItHasBeenCheckedError
أحد أكثر الأخطاء المخيفة في التطوير الزاوي. لحسن الحظ ، يقوم Ivy بإخراج الخطأ بطريقة أوضح ، مما يسهل العثور على مصدر المشكلة.
لذلك ، دعونا نقدم خطأ ExpressionChangedAfterItHasBeenCheckedError
. للقيام بذلك ، أولاً ، سننشئ خدمة:
ng gs Title
بعد ذلك ، سنضيف BehaviorSubject
وطرق للوصول إلى Observable
قيمة جديدة.

export class TitleService { private bs = new BehaviorSubject < string > (''); constructor() {} get title$() { return this.bs.asObservable(); } update(title: string) { this.bs.next(title); } }
بعد ذلك ، سنضيف هذا إلى app.component.html
:
<!-- ... --> <div class="col-6"> <h2> {{title$ | async}} </h2> <app-tv-rating-form [title]="title"></app-tv-rating-form> </div>
وفي app.component.ts
، سنقوم بحقن TitleService
:
export class AppComponent implements OnInit { // ... title$: Observable < string > ; constructor( private titleSvc: TitleService ) {} ngOnInit() { this.title$ = this.titleSvc.title$; } // ... }
أخيرًا ، في tv-rating-form.component.ts
، سنقوم بحقن TitleService
وتحديث عنوان AppComponent
، والذي سيرمي خطأ ExpressionChangedAfterItHasBeenCheckedError
.
// ... constructor( private titleSvc: TitleService ) { } ngOnInit() { this.titleSvc.update('new title!'); }
يمكننا الآن رؤية الخطأ المفصل في وحدة تحكم مطور المتصفح ، والنقر على app.component.html
إلى مكان الخطأ.
يمكننا إصلاح هذا الخطأ عن طريق تغليف استدعاء الخدمة بـ setTimeout
:
setTimeout(() => { this.titleSvc.update('new title!'); });
لفهم سبب حدوث خطأ ExpressionChangedAfterItHasBeenCheckedError
واستكشاف الاحتمالات الأخرى ، فإن مشاركة مكسيم كوريتسكي حول هذا الموضوع تستحق القراءة.
تسمح لنا Angular Ivy بعرض الأخطاء بطريقة أوضح وتساعد في فرض كتابة TypeScript في التعليمات البرمجية الخاصة بنا. في القسم التالي ، سنغطي بعض السيناريوهات الشائعة حيث سنستفيد من Ivy وتصحيح الأخطاء.
كتابة اختبار لتطبيق Angular 9 الخاص بنا باستخدام أدوات تسخير المكونات
في Angular 9 ، تم تقديم واجهة برمجة تطبيقات اختبار جديدة تسمى أحزمة المكونات . الفكرة من وراءها هي إزالة كل الأعمال الروتينية المطلوبة للتفاعل مع DOM ، مما يجعل العمل معها أسهل وأكثر استقرارًا في التشغيل.
يتم تضمين واجهة برمجة تطبيقات تسخير المكونات في مكتبة @angular/cdk
، لذلك دعونا أولاً نثبتها على مشروعنا:
npm install @angular/cdk
الآن يمكننا كتابة اختبار ومكونات عنصر الرافعة المالية. في tv-rating-form.component.spec.ts
، فلنقم بإعداد الاختبار:
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(); })); // ... });
بعد ذلك ، دعنا ننفذ ComponentHarness
لمكوننا. سنقوم بإنشاء اثنين من الأدوات: أحدهما لـ TvRatingForm
والآخر لـ NgbRating
. يتطلب ComponentHarness
حقلاً static
، hostSelector
، والذي يجب أن يأخذ قيمة محدد المكون.
// ... 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'; } // ...
بالنسبة إلى TvRatingFormHarness
بنا ، سننشئ محددًا لزر الإرسال ووظيفة لتحريك click
. يمكنك أن ترى مدى سهولة تنفيذ ذلك.
class TvRatingFormHarness extends ComponentHarness { // ... protected getButton = this.locatorFor('button'); async submit() { const button = await this.getButton(); await button.click(); } }
بعد ذلك ، سنضيف طرقًا لتحديد التصنيف. هنا نستخدم locatorForAll
للبحث عن جميع <span>
العناصر التي تمثل النجوم التي يمكن للمستخدم النقر عليها. تحصل وظيفة rate
فقط على جميع نجوم التقييمات الممكنة وتنقر على التصنيف المقابل للقيمة المرسلة.
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(); } }
آخر قطعة مفقودة هي توصيل TvRatingFormHarness
بـ NgbRatingHarness
. للقيام بذلك ، نقوم فقط بإضافة محدد المواقع في فئة TvRatingFormHarness
.
class TvRatingFormHarness extends ComponentHarness { // ... getRating = this.locatorFor(NgbRatingHarness); // ... }
الآن ، دعنا نكتب اختبارنا:
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}'); }); });
لاحظ أنه بالنسبة select
الخاص بنا داخل النموذج ، لم نقم بتنفيذ تحديد قيمته عبر أداة تسخير. ذلك لأن واجهة برمجة التطبيقات (API) ما زالت لا تدعم تحديد خيار. لكن هذا يمنحنا فرصة لمقارنة ما بدا عليه التفاعل مع العناصر قبل استخدام المكونات.
شيء أخير قبل أن نجري الاختبارات. نحتاج إلى إصلاح app.component.spec.ts
نظرًا لأننا قمنا بتحديث title
ليكون 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); }); });
الآن ، عندما نجري ng test
، ينجح اختبارنا.
العودة إلى تطبيق Angular 9 النموذجي: حفظ البيانات في قاعدة بيانات
دعنا نختتم برنامج Angular 9 التعليمي الخاص بنا عن طريق إضافة اتصال إلى Firestore وحفظ التصنيفات في قاعدة البيانات.
للقيام بذلك ، نحتاج إلى إنشاء مشروع Firebase. بعد ذلك ، سنقوم بتثبيت التبعيات المطلوبة.
npm install @angular/fire firebase
في إعدادات مشروع Firebase Console ، سنحصل على تكوينه وإضافتهما إلى environment.ts
و 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}' } };
بعد ذلك ، سنقوم باستيراد الوحدات الضرورية في 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, ], // ... })
بعد ذلك ، في tv-rating-form.component.ts
، سنقوم بحقن خدمة AngularFirestore
وحفظ تصنيف جديد عند إرسال النموذج:
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(); } }
الآن ، عندما ننتقل إلى Firebase Console ، سنرى العنصر الذي تم إنشاؤه حديثًا.
أخيرًا ، دعنا ندرج جميع التصنيفات في AppComponent
. للقيام بذلك ، في app.component.ts
، سنحصل على البيانات من المجموعة:
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(); } }
... وفي app.component.html
، سنضيف قائمة بالتقييمات:
<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>
هذا ما يبدو عليه تطبيق Angular 9 التعليمي الخاص بنا عندما يتم تجميعه معًا.
Angular 9 and Angular Ivy: تطوير أفضل وتطبيقات أفضل وتوافق أفضل
في هذا البرنامج التعليمي Angular 9 ، قمنا بتغطية بناء نموذج أساسي وحفظ البيانات في Firebase واسترداد العناصر منه.
على طول الطريق ، رأينا التحسينات والميزات الجديدة المضمنة في Angular 9 و Angular Ivy. للحصول على قائمة كاملة ، يمكنك التحقق من أحدث إصدار من مدونة Angular الرسمية.
كشريك Google Cloud Partner ، يتوفر خبراء Toptal المعتمدون من Google للشركات عند الطلب لأهم مشاريعهم.