جميع الامتيازات ، بلا متاعب: درس 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 ونظافة التعليمات البرمجية.

نتيجة لذلك ، لدينا مجلد وملفات مساحة عمل أساسية.

لقطة شاشة لـ IDE تعرض مجلد التطبيق ng9 ، الذي يحتوي على node_modules و .editorconfig و .gitignore و angular.json و package-lock.json و package.json و README.md و tsconfig.json و tslint.json.

الآن ، دعنا نضيف تطبيقًا جديدًا. للقيام بذلك ، سنقوم بتشغيل:

 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 ، فسنرى التطبيق يعمل بسقالاته الأولية.

لقطة شاشة لسقالات Angular 9 ، مع ملاحظة أن "تطبيق تقييم البرامج التلفزيونية قيد التشغيل!" هناك أيضًا روابط إلى الموارد والخطوات التالية.

إذا قمنا بتشغيل ng build --prod ، فيمكننا رؤية قائمة الملفات التي تم إنشاؤها.

لقطة شاشة لإخراج "ng build --prod" الخاص بـ Angular 9. يبدأ بـ "إنشاء حزم ES5 للتحميل التفاضلي ..." بعد الانتهاء من ذلك ، فإنه يسرد العديد من أجزاء ملفات JavaScript - وقت التشغيل ، و polyfills ، و main ، كل منها بإصدار -es2015 و -es5- وملف CSS واحد. يعطي السطر الأخير طابعًا زمنيًا وتجزئة ووقت تشغيل يبلغ 23881 مللي ثانية.

لدينا نسختان من كل ملف. أحدهما متوافق مع المتصفحات القديمة ، والآخر تم تجميعه لاستهداف ES2015 ، والذي يستخدم واجهات برمجة تطبيقات أحدث ويتطلب عددًا أقل من polyfill للتشغيل على المتصفحات.

أحد التحسينات الكبيرة في Angular 9 هو حجم الحزمة. وفقًا لفريق Angular ، يمكنك أن ترى انخفاضًا يصل إلى 40٪ في التطبيقات الكبيرة.

بالنسبة إلى تطبيق تم إنشاؤه حديثًا ، فإن حجم الحزمة يشبه إلى حد كبير حجم Angular 8 ، ولكن مع نمو تطبيقك ، ستلاحظ أن حجم الحزمة أصبح أصغر مقارنة بالإصدارات السابقة.

ميزة أخرى تم تقديمها في Angular 9 هي القدرة على تحذيرنا إذا كان أي من ملفات CSS ذات النمط المكون أكبر من عتبة محددة.

لقطة شاشة لقسم "الميزانيات" في ملف تكوين Angular 9 JSON ، مع كائنين في مصفوفة. الكائن الأول له "نوع" معيّن على "أولي" ، و "maxWarning" معيّن على "2mb" ، و "maxError" معيّن على "5mb." الكائن الثاني لديه "type" مضبوط على "anyComponentStyle" ، و "maxWarning" مضبوط على "6kb" ، و "maxError" مضبوط على "10kb."

سيساعدنا هذا في التعرف على عمليات الاستيراد ذات الأنماط السيئة أو ملفات نمط المكونات الضخمة.

إضافة نموذج لتقييم العروض التليفزيونية

بعد ذلك ، سنضيف نموذجًا لتقييم البرامج التلفزيونية. لذلك ، سنقوم أولاً بتثبيت 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 يعرض نموذجًا بعنوان "TV SHOW" ، مع قائمة منسدلة تسرد عددًا قليلاً من عناوين البرامج ، ومقياس النجوم ، وزر موافق. في الرسم المتحرك ، يقوم المستخدم بتحديد عرض ، وتحديد تصنيف ، ثم النقر فوق الزر "موافق".

قبل المضي قدمًا ، دعنا نلقي نظرة سريعة على بعض ميزات 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' } ];

... سنحصل على خطأ من المترجم.

لقطة شاشة لمخرج مترجم Angular 9 / Angular Ivy ، مع اسم ملف وموضع ، يقول "خطأ TS2339: الخاصية 'name' غير موجودة في النوع '{title: string؛}'." كما يعرض أيضًا سطر الكود المعني ويضع خطًا تحت المرجع ، في هذه الحالة في ملف tv-rating-form.component.html حيث تم ذكر tvShow.name. بعد ذلك ، يتم تتبع المرجع إلى ملف HTML هذا إلى ملف TypeScript المقابل ويتم تمييزه بالمثل.

سيسمح لنا فحص النوع هذا بمنع الأخطاء الإملائية والاستخدام غير الصحيح لأنواع 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; }

إذا قمنا بإجراء هذه التغييرات الثلاثة ، فسنحصل على نوع آخر من الخطأ من المترجم.

لقطة شاشة لمخرج مترجم Angular 9 / Angular Ivy ، بتنسيق مشابه للتنسيق السابق ، مع تمييز app.component.html بـ "خطأ TS 2322: النوع" فارغ "غير قابل للتخصيص لكتابة" سلسلة "."

في حال أردنا تجاوزه ، يمكننا استخدام $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 إلى مكان الخطأ.

لقطة شاشة لوحدة تحكم مطور المتصفح ، تعرض الإبلاغ Angular Ivy عن خطأ ExpressionChangedAfterItHasBeenCheckedError. يعطي تتبع المكدس بنص أحمر الخطأ ، بالإضافة إلى القيم السابقة والحالية ، وتلميحًا. في منتصف تتبع المكدس يوجد السطر الوحيد الذي لا يشير إلى core.js. ينقر المستخدم عليه ويتم توجيهه إلى سطر 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 ، ينجح اختبارنا.

لقطة شاشة لـ Karma وهي تجري اختبارات على تطبيق Angular 9 الخاص بنا. يظهر "Ran 2 من 6 مواصفات" مع الرسالة "غير مكتمل: تم العثور على fit () أو fdescribe () ، مواصفات 2 ، 0 فشل ، عشوائي مع البذور 69573." تم تمييز الاختبارين الخاصين بـ TvRatingFormComponent. الاختبارات الثلاثة الخاصة بـ AppComponent والاختبار الوحيد لـ TitleService كلها رمادية اللون.

العودة إلى تطبيق 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(); } } 

لقطة شاشة لتطبيق تعليمي Angular 9 تعرض نموذجًا بعنوان "TV SHOW" أسفل عنوان صفحة أكبر "عنوان جديد!" مرة أخرى ، يحتوي على قائمة منسدلة تسرد عددًا قليلاً من عناوين العرض ، ومقياس النجوم ، وزر موافق ، ومرة ​​أخرى يختار المستخدم عرضًا ، ويختار تصنيفًا ، ثم ينقر فوق الزر موافق.

الآن ، عندما ننتقل إلى Firebase Console ، سنرى العنصر الذي تم إنشاؤه حديثًا.

لقطة شاشة لوحدة تحكم Firebase. يوجد في العمود الأيمن معمل joaq مع بعض المجموعات: الحضور ، والأجناس ، والتصنيفات ، والاختبار ، والمستخدمون. يتم تحديد عنصر التصنيفات ويتم تمييزه في العمود الأوسط بمعرف محدد - إنه المستند الوحيد. يُظهر العمود الأيمن حقلين: تم تعيين "التصنيف" على 4 ، وتم تعيين "tvShow" على "Mad men".

أخيرًا ، دعنا ندرج جميع التصنيفات في 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 تعرض نموذجًا بعنوان "TV SHOW" أسفل عنوان صفحة أكبر "عنوان جديد!" مرة أخرى ، يحتوي على قائمة منسدلة تسرد عددًا قليلاً من عناوين العرض ، ومقياس النجوم ، وزر موافق. هذه المرة ، يسرد العمود الأيمن بالفعل "Mad men (4)" ، ويقيم المستخدم "Lost" بثلاث نجوم ، متبوعًا بـ "Mad men" مرة أخرى بأربع نجوم. يظل العمود الأيمن مرتبًا أبجديًا بعد كلا التصنيفين الجديدين.

Angular 9 and Angular Ivy: تطوير أفضل وتطبيقات أفضل وتوافق أفضل

في هذا البرنامج التعليمي Angular 9 ، قمنا بتغطية بناء نموذج أساسي وحفظ البيانات في Firebase واسترداد العناصر منه.

على طول الطريق ، رأينا التحسينات والميزات الجديدة المضمنة في Angular 9 و Angular Ivy. للحصول على قائمة كاملة ، يمكنك التحقق من أحدث إصدار من مدونة Angular الرسمية.


شارة شريك Google Cloud.

كشريك Google Cloud Partner ، يتوفر خبراء Toptal المعتمدون من Google للشركات عند الطلب لأهم مشاريعهم.