اذهب إلى المحتوى

كيفية استعمال Angular في بناء تطبيقات الويب


أحمد سلهب

يقدِّم لك هذا المقال التعليمي الأساسيات الموجودة في Angular من خلال السير معك في عملية بناء موقع تجارة إلكترونية مؤلف من قائمة من المنتجات، وسلة التسوق، ونموذج الدفع، كما يستخدِم هذا المقال تطبيقًا جاهزًا يمكنك معاينته وتعديله بصورة تفاعلية عن طريق StackBlitz دون الحاجة إلى إعداد بيئة عمل محلية، حيث تُعَدّ StackBlitz بيئة تطوير تعتمد على المتصفح وتمكِّنك من إنشاء وحفظ ومشاركة المشاريع باستخدام مجموعة متنوعة من التقنيات.

هذه المقال جزءٌ من سلسلة مقالات حول بناء تطبيق تجارة إلكترونية عبر Angular:

المتطلبات الأساسية

يجب أن يكون لديك فهم أساسي لما يلي لتحقيق أعلى إفادة من هذا المقال التعليمي:

تفاصيل التطبيق الذي سنبنيه

تُبنَى تطبيقات Angular بالاعتماد على المكوِّنات، إذ تمثِِّل هذه المكوِّنات اللبنات الأساسية لبناء واجهة المستخدِم والتي تتيح لك إعادة استخدام مجموعات كبيرة من وظائف واجهة المستخدِم، ويتألف المكوِّن من ثلاثة أشياء:

  • صنف المكوِّن الذي يعالِج البيانات والوظائف.
  • قالب HTML الذي يحدِّد واجهة المستخدِم.
  • التنسيقات الخاصة بالمكوِّن التي تحدِّد الشكل والمظهر.

يوضح هذا الدليل كيفية بناء تطبيق باستخدام المكوِّنات التالية:

  • <app-root>: المكوِّن الجذر الذي يُحمَّل أولًا والذي يمثِّل الحاوية للمكوِّنات الأخرى.
  • <app-top-bar>: اسم المتجر وزر الدفع.
  • <app-product-list>: قائمة المنتجات.
  • <app-product-alerts> - مكوِّن يحتوي على تنبيهات التطبيق.

001.png

البدء مع الملفات الأساسية للمشروع

ولِّد مشروع العينة الجاهزة في StackBlitz لإنشاء الملفات الأساسية التي سنتعامل معها، واتبع ما يلي لحفظ عملك:

  1. سجّل الدخول إلى StackBlitz.
  2. انسخ المشروع الذي ولَّدته.
  3. احفظ المشروع بصورة دورية.

يعرض جزء المعاينة الموجود على اليمين في StackBlitz حالة بدء تطبيق المثال، كما تعرض المعاينة منطقتين هما:

  • My Store شريط علوي يحوي اسم المتجر وزر الدفع.
  • Products ترويسة من أجل قائمة المنتجات.

002.gif

يعرض جزء المشروع الموجود على اليسار الملفات المصدرية التي يتكون منها التطبيق، كما تتضمن ملفات البنية التحتية وملفات الضبط، في حين ينشئ StackBlitz ملفات أساسية وبيانات وهمية لك عندما تولِّد تطبيقات المثال في StackBlitz والتي تأتي مترافقة مع المقالات التعليمية، كما توجد جميع الملفات التي ستستخدمها خلال المقال التعليمي ضمن مجلد src، ويمكنك الاطلاع على التوثيق الخاص بـ StackBlitz لمزيد من المعلومات حول استخدام StackBlitz.

إنشاء قائمة المنتجات

سنعمل في هذا القسم على تحديث التطبيق لعرض قائمة المنتجات، حيث سنستخدم بيانات المنتج المعرَّفة مسبقًا في ملف products.ts والتوابع الموجودة في ملف product-list.component.ts، كما سيرشدك هذا القسم خلال عملية تحرير ملف HTML والمعروف باسم القالب أيضًا.

بدايةً، افتح ملف القالب product-list.component.html في مجلد product-list وأضف موجِّهًا هيكليًا ngFor للعنصر <div>، كما يلي:

  • الملف src/app/product-list/product-list.component.html:
<h2>Products</h2>

<div *ngFor="let product of products">
</div>

سيتكرر العنصر <div> من أجل كل منتج product في قائمة المنتجات products باستخدام ngFor*، كما تشكّل الموجّهات الهيكلية Structural directives، أو تعيد تشكيل بنية DOM عن طريق إضافة العناصر وإزالتها ومعالجتها.

بعد ذلك أضف داخل العنصر <div> العنوان <h3> وضع فيه {{ product.name }} الذي يمثل اسم المنتج وهو مثال عن سياق الإدراج في Angular، حيث يتيح لك الإدراج {{ }} تصيير قيمة الخاصية على أساس نص.

  • الملف src/app/product-list/product-list.component.html:
<h2>Products</h2>

<div *ngFor="let product of products">

  <h3>
      {{ product.name }}
  </h3>

</div>

يُحدَّث جزء المعاينة لعرض اسم كل منتج في القائمة.

003.png

أضف العنصر <a> حول {{ product.name }} لربط اسم كل منتج بتفاصيله ثم اضبط العنوان ليصبح اسم المنتج باستخدام صياغة التربيط بين الخاصيات Property binding [] كما يلي:

  • الملف src/app/product-list/product-list.component.html:
<h2>Products</h2>

<div *ngFor="let product of products">

  <h3>
    <a [title]="product.name + ' details'">
      {{ product.name }}
    </a>
  </h3>

</div>

مرّر مؤشر الفأرة فوق اسم منتج ما في جزء المعاينة لترى قيمة خاصية التربيط بالاسم، والتي تمثّل اسم المنتج بالإضافة إلى كلمة "تفاصيل". يتيح لك التربيط عن طريق الصياغة [] استخدام قيمة خاصية مُعرَّفة أو موجودة في القالب.

004.png

أضف توصيفات المنتجات عن طريق استخدام الموجِّه ‎*ngIf على عنصر <p>، حيث ينشئ Angular عنصر <p> فقط في حال كان المنتج الحالي يملك وصفًا.

  • الملف src/app/product-list/product-list.component.html:
<h2>Products</h2>

<div *ngFor="let product of products">

  <h3>
    <a [title]="product.name + ' details'">
      {{ product.name }}
    </a>
  </h3>

  <p *ngIf="product.description">
    Description: {{ product.description }}
  </p>

</div>

يعرض التطبيق الآن اسم ووصف كل منتج في القائمة، ولاحظ أنّ المنتج الأخير لا يملك فقرة وصف أي لا يملك قيمةً للخاصية description وبالتالي لم تنشئ Angular عنصر <p> له.

005.png

أخيرًا أضف زرًا حتى يتمكّن المستخدِمون من مشاركة المنتج واربط الحدث click مع التابع ()share في ملف product-list.component.ts، ويكون التربيط بين الأحداث بوضع اسم الحدث بين قوسين () حول الحدث كما هو الحال في حدث (click) ضمن عنصر <button>.

  • الملف src/app/product-list/product-list.component.html:
<h2>Products</h2>

<div *ngFor="let product of products">

  <h3>
    <a [title]="product.name + ' details'">
      {{ product.name }}
    </a>
  </h3>

  <p *ngIf="product.description">
    Description: {{ product.description }}
  </p>

  <button (click)="share()">
    Share
  </button>

</div>

يملك كل منتج الآن زر مشاركة Share.

006.png

تطلق عملية النقر على زر المشاركة تنبيهًا يخبرنا بأنه "شورِك المنتج!".

007.png

بهذا تكون قد استكشفت الآن الميزات الشائعة الاستخدام أثناء العمل في قوالب Angular.

تمرير البيانات إلى مكون ابن

تعرض حاليًا قائمة المنتجات اسم وتوصيف كل منتج، كما يعرِّف ProductListComponent أيضًا خاصية products التي تحتوي على بيانات مستورَدة لكل منتج من مصفوفة products في products.ts، وتكون الخطوة التالية هي إنشاء ميزة تنبيه جديدة تستخدِم بيانات المنتج من ProductListComponent.

يتحقّق التنبيه من سعر المنتج، فإذا كان السعر أكبر من 700 دولار، سيعرض زر نبهني Notify Me الذي يتيح للمستخدِمين الاشتراك في الإشعارات عندما يُطرَح المنتج للبيع، كما سيرشدك هذا القسم خلال عملية إنشاء مكوِّن ابن ProductAlertsComponent يمكنه تلقي البيانات من المكوِّن الأب ProductListComponent.

بدايةً، انقر بزر الفأرة الأيمن على مجلد app واستخدم المولد الخاص بـ Angular لإنشاء مكون جديد يُسمّى product-alerts.

008.png

ينشئ المولِّد ملفات البدء الأساسية لأجزاء المكوِّن الثلاثة:

  • product-alerts.component.ts
  • product-alerts.component.html
  • product-alerts.component.css

افتح ملف product-alerts.component.ts وستجد فيه مزخرفًا decorator باسم ‎@Component()‎ يشير إلى أن الصنف الحالي هو مكوِّن، كما يوفِّر بيانات وصفية حوله بما في ذلك المحدِّد selector والقوالب templates والتنسيقات styles.

  • الملف src/app/product-alerts/product-alerts.component.ts:
import { Component, OnInit } from '@angular/core';

@Component
({
  selector: 'app-product-alerts',
  templateUrl: './product-alerts.component.html',
  styleUrls: ['./product-alerts.component.css']
})
export class ProductAlertsComponent implements OnInit {
  constructor() { }

  ngOnInit() {
  }

}

تتمثَّل الميزات الرئيسية في ‎@Component()‎ بما يلي:

  • يعرّف المحدد selector اسم المكون وقيمته هنا app-product-alerts، كما تعارف مبرمجو Angular على بدء اسم المكون بالبادئة -app متبوعة باسم المكوِّن.
  • تشير أسماء ملفات القوالب والتنسيقات إلى HTML وCSS للمكوِّن.
  • يصدِّر تعريف ‎@Component()‎ الصنف ProductAlertsComponent أيضًا والذي يعالِج وظائف المكوِّن.

ستورد Input من ‎@angular/core أولًا لإعداد مكوِّن ProductAlertsComponent لاستقبال بيانات المنتج.

  • الملف src/app/product-alerts/product-alerts.component.ts:
import { Component, OnInit } from '@angular/core';
import { Input } from '@angular/core';
import { Product } from '../products';

أضف في تعريف الصنف ProductAlertsComponent خاصيةً تسمى product مع مزخرف ‎@Input()‎ والذي يشير إلى تمرير قيمة الخاصية إلى المكوِّن الرئيسي ProductListComponent.

  • الملف src/app/product-alerts/product-alerts.component.ts:
export class ProductAlertsComponent implements OnInit {
  @Input() product!: Product;
  constructor() { }

  ngOnInit() {
  }

}

افتح الملف product-alerts.component.html واستبدل فقرة العنصر النائب بزر نبّهني Notify Me الذي يظهر في حال كان سعر المنتج أكثر من 700 دولار.

  • الملف src/app/product-alerts/product-alerts.component.html:
<p *ngIf="product && product.price > 700">
  <button>Notify Me</button>
</p>

لاحظ أن المولد يضيف تلقائيًا الصنف ProductAlertsComponent إلى الوحدة AppModule ليكون متاحًا في كافة مكونات التطبيق:

import { ProductAlertsComponent } from './product-alerts/product-alerts.component';

@NgModule({
  declarations: [
    AppComponent,
    TopBarComponent,
    ProductListComponent,
    ProductAlertsComponent,
  ],

أضف المحدد <app-product-alerts> إلى product-list.component.html لعرض ProductAlertsComponent على أساس ابن للمكوِّن ProductListComponent، ومرِّر المنتج الحالي على أساس دخل إلى المكوِّن باستخدام التربيط عن طريق تربيط الخاصية property binding.

  • الملف src/app/product-list/product-list.component.html:
<button (click)="share()">
  Share
</button>

<app-product-alerts
  [product]="product">
</app-product-alerts>

يأخذ مكوِّن التنبيه الجديد الخاص بالمنتج منتجًا من قائمة المنتج على أساس دخل له، حيث يعرض أو يخفي عن طريقه زر نبهني Notify Me بناءً على قيمة سعر هذا المنتج، فسعر هاتف Phone XL مثلًا، هو أكثر من 700 دولار لذلك عُرِض زر نبهني Notify Me لهذا المنتج.

009.png

تمرير البيانات إلى المكون الرئيسي

يحتاج المكون الابن إلى التنبيه وإرسال البيانات إلى المكوِّن الأب لكي يعمل زر نبهني Notify Me، كما يحتاج ProductAlertsComponent إلى إطلاق حدث عندما ينقر المستخدِم على نبهني Notify Me، في حين يحتاج ProductListComponent إلى الاستجابة للحدث.

اقتباس

توضيح: يتضمن مولد Angular في المكونات الحديثة بانيًا فارغًا constructor()‎ وواجهة OnInit وتابعngOnInit()‎، وبما أنَّ الخطوات التالية لا تستخدِم ما سبق فقد حُذِفوا من شيفرات الأمثلة للاختصار.

أولًا، استورد Output و EventEmitter في الملف product-alerts.component.ts من ‎@angular/core.

  • الملف src/app/product-alerts/product-alerts.component.ts:
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Product } from '../products';

ثانيًا، عرِّف خاصيةً تسمى notify في صنف المكوِّن مع مزخرف ‎@Output()‎ ونسخة من EventEmitter()‎، هيِّئ المكوِّن ProductAlertsComponent باستعمال ‎@Output()‎ لكي تسمح لمكوِّن ProductAlertsComponent بإطلاق حدث عندما تتغير قيمة الخاصية notify.

  • الملف src/app/product-alerts/product-alerts.component.ts:
export class ProductAlertsComponent {
  @Input() product: Product|undefined;
  @Output() notify = new EventEmitter();
}

ثالثًا، عدِّل زر نبهني Notify Me ضمن product-alerts.component.html باستخدام التربيط مع الحدث من أجل استدعاء الدالة notify.emit()‎.

  • الملف src/app/product-alerts/product-alerts.component.html:
<p *ngIf="product && product.price > 700">
  <button (click)="notify.emit()">Notify Me</button>
</p>

رابعًا، عرِّف سلوكًا يحدث عندما ينقر المستخدِم على الزر، حيث يبدأ المكون الأب ProductListComponent وليس المكون ProductAlertsComponent بالعمل عندما يولِّد الابن الحدث. عرّف تابع onNotify()‎ ضمن product-list.component.ts بطريقة مشابهة للتابعshare()‎.

  • الملف src/app/product-list/product-list.component.ts:
export class ProductListComponent {
  products = products;

  share() {
    window.alert('The product has been shared!');
  }

  onNotify() {
    window.alert('You will be notified when the product goes on sale');
  }
}

خامسًا، حدِّث ProductListComponent ليستقبل البيانات من ProductAlertsComponent، واربط <app-product-alerts> مع التابع onNotify()‎ الموجود في مكون قائمة المنتجات ضمن الملف product-list.component.html، حيث ما يعرضه الزر نبهني Notify Me هو <app-product-alerts>.

  • الملف src/app/product-list/product-list.component.html:
<button (click)="share()">
  Share
</button>

<app-product-alerts
  [product]="product" 
  (notify)="onNotify()">
</app-product-alerts>

سادسًا، انقر على زر نبهني Notify Me لإطلاق التنبيه والذي يطبع رسالة "سوف تُعلَم عندما يُطرح المنتج للبيع" على المتصفح.

010.png

الخطوة التالية

أنشأتَ في هذا المقال تطبيقًا يمرّ على مجموعة من البيانات، كما أنشأتَ مكوِّنات مميزةً تتواصل مع بعضها، وسنكمل في المقال التالي استشكاف Angular وإنشاء التطبيق الخاص بك عبر مقال إضافة التنقل وإدارة البيانات في تطبيق Angular لإنشاء صفحة تفاصيل المنتج وقائمة المنتجات قبل أن ننتقل إلى خطوة نشر التطبيق على خادم الويب أو أي خادم تريد.

ترجمة -وبتصرف- للمقال Getting started with Angular من موقع angular.io الرسمي.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...