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

إضافة التنقل وإدارة البيانات في تطبيق Angular


أحمد سلهب

بُني هذا المقال على الخطوة الأولى من سلسلة المقالات التعليمية لتعلم استعمال Angular عبر بناء تطبيق تجارة إلكترونية، حيث يحتوي تطبيق المتجر عبر الانترنت في هذه المرحلة من التطوير على قائمة المنتجات الأساسية، كما ستضيف في الأقسام التالية الميزات التالية إلى التطبيق:

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

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

ربط مسار URL مع مكون

يستخدم التطبيق الموجِّه Router من Angular للانتقال إلى المكوِّن ProductListComponent افتراضيًا، حيث يوضِّح هذا القسم كيفية تعريف الوجهة لعرض تفاصيل المنتجات بصورة فردية.

نبدأ أولًا بتوليد مكون جديد باسم product-details لعرض تفاصيل المنتج وذلك بتنفيذ الأمر التالي:

ng generate component product-details

بعد ذلك أضف وجهة لتفاصيل المنتج في app.module.ts من خلال استخدام path مع القيمة products/:productId وضبط component إلى المكون ProductDetailsComponent.

  • الملف src/app/app.module.ts:
@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    RouterModule.forRoot([
      { path: '', component: ProductListComponent },
      { path: 'products/:productId', component: ProductDetailsComponent },
    ])
  ],

افتح product-list.component.html، وعدِّل المِربط الخاص باسم المنتج ليتضمن routerLink مع استخدام product.id على أساس معامِل.

  • الملف src/app/product-list/product-list.component.html:
<div *ngFor="let product of products">

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

<!-- . . . -->

</div>

يساعدك الموجِّه RouterLink على تخصيص الرابط، حيث يحتوي الموجه أو العنوان في هذه الحالة على جزء واحد ثابت هو ‎/products، ويكون الجزء الأخير متغيرًا يتمثَّل في إدراج الخاصية id للمنتج الحالي، حيث سيكون العنوان URL مثلًا لمنتج يملك المُعرِّف 1 مشابهًا للعنوان https://getting-started-myfork.stackblitz.io/products/1

أخيرًا، تحقّق من عمل الموجِّه على النحو المطلوب من خلال النقر على اسم المنتج، حيث يجب أن يعرض التطبيق المكوِّن ProductDetailsComponent والذي سيعرض رسالة "product-details works" أي صفحة تفاصيل المنتج تعمل!، ولاحظ أنَّ العنوان في نافذة المعاينة يتغير، كما يكون الجزء الأخير هو products/#‎ حيث تمثّل # رقم المسار الذي نقرت عليه.

007.png

عرض تفاصيل المنتج

يعالِج ProductDetailsComponent طريقة عرض كل منتج، يعرض موجِّه Angular المكوِّنات اعتمادًا على عنوان المتصفح والمسارات التي حددتها، كما ستستخدِم في هذا القسم موجِّه Angular لدمج بيانات المنتجات products ومعلومات المسار لعرض التفاصيل الخاصة لكل منتج.

بدايةً، استورد ActivatedRoute من ‎@angular/router ومصفوفة المنتجات products من ‎../products في ملف product-details.component.ts.

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

import { Product, products } from '../products';

حدِّد خاصية product.

  • الملف src/app/product-details/product-details.component.ts:
export class ProductDetailsComponent implements OnInit {
  product: Product|undefined;
  /* ... */
}

احقن ActivatedRoute في الباني constructor()‎ عن طريق إضافة private route: ActivatedRoute على أساس وسيط ضمن أقواس الباني.

  • الملف src/app/product-details/product-details.component.ts:
export class ProductDetailsComponent implements OnInit {
  product: Product|undefined;

  constructor(private route: ActivatedRoute) { }

}

يوجد صنف يدعى ActivatedRoute لكل مكوِّن يحمِّله موجّه Angular بحيث يحتوي على معلومات حول الموجه route ومعامِلاته، كما تُهيّئ المكوِّن ليستخدِم خدمة عن طريق حقن ActivatedRoute، وسنغطي ذلك في خطوة إدارة البيانات بتفصيل أكثر.

استخرج productId من معامِلات المسار في دالة ngOnInit()‎ واعثر على المنتج المقابل له ضمن مصفوفة products.

  • الملف src/app/product-details/product-details.component.ts:
ngOnInit() {
  // First get the product id from the current route.
  const routeParams = this.route.snapshot.paramMap;
  const productIdFromRoute = Number(routeParams.get('productId'));

  // Find the product that correspond with the id provided in route.
  this.product = products.find(product => product.id === productIdFromRoute);
}

تقابل معامِلات المسار متغيرات المسار التي حدَّدتها في المسار، حيث نستخدِم route.snapshot للوصول إلى معاملات الموجه والذي يُعَدّ ActivatedRouteSnapshot ويحوي معلومات حول الموجه النشط في لحظة معينة من الزمن، كما يوفِّر العنوان الذي يطابق موجه productId، حيث يستخدِم Angular معرف المنتج productId لعرض تفاصيل كل منتج فريد.

حدِّث قالب ProductDetailsComponent لعرض تفاصيل المنتج باستخدام ‎*ngIf، فإذا كان المنتج منتج موجودًا، فسيعرض محتوى <div> مع اسم وسعر وتوصيف المنتج.

  • الملف src/app/product-details/product-details.component.html
<h2>Product Details</h2>

<div *ngIf="product">
  <h3>{{ product.name }}</h3>
  <h4>{{ product.price | currency }}</h4>
  <p>{{ product.description }}</p>

</div>

يستخدِم السطر <h4>{{ product.price | currency }}</h4> الأنابيب العملة currency لتحويل قيمة product.price من عدد إلى سلسلة نصية بصيغة عملة، حيث يُعَدّ الأنبوب طريقةً يمكنك من خلالها تحويل البيانات الموجودة في قالب HTML الخاص بك.

عندما ينقر المستخدمون على اسم في قائمة المنتجات، ينقلهم الموجِّه إلى العنوان المميز للمنتج ويعرِض ProductDetailsComponent ويعرض تفاصيل المنتج.

008.png

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

إنشاء خدمة سلة التسوق

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

تعريف خدمة السلة

لإنشاء خدمة السلة، استعمل الطرفية ونفذ الأمر التالي لتوليد خدمة باسم cart:

ng generate service cart
  • الملف src/app/cart.service.ts:
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class CartService {

  constructor() {}

}

استورد واجهة Product من ‎./products.ts ضمن cart.service.ts، وعرّف خاصيةً باسم items في صنف CartService لتخزين مصفوفة المنتجات الحالية الموجودة في سلة التسوق.

  • الملف src/app/cart.service.ts:
import { Product } from './products';
/* . . . */
export class CartService {
  items: Product[] = [];
/* . . . */
}

عرِّف التوابع المسؤولة عن إضافة عناصر إلى سلة التسوق وإعادة عناصر سلة التسوق ومسح عناصر سلة التسوق.

  • الملف src/app/cart.service.ts:
export class CartService {
  items: Product[] = [];
/* . . . */

  addToCart(product: Product) {
    this.items.push(product);
  }

  getItems() {
    return this.items;
  }

  clearCart() {
    this.items = [];
    return this.items;
  }
/* . . . */
}

شرح التوابع السابقة المعرفة:

  • addToCart()‎: يضيف منتجًا إلى مصفوفة items.
  • getItems()‎: يجمِّع العناصر التي يضيفها المستخدمون إلى سلة التسوق ويُعيد كل عنصر مع الكمية المرتبطة به.
  • clearCart()‎: يعيد مصفوفةً فارغةً من العناصر، أي أنه يُفرِّغ السلة من العناصر الموجودة فيها.

استخدم خدمة السلة

سنبرمج الآن عملية إضافة منتج إلى السلة عن طريق استخدام الخدمة CartService.

أولًا، استورد خدمة السلة في ملف product-details.component.ts.

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

import { Product, products } from '../products';
import { CartService } from '../cart.service';

ثانيًا، احقن خدمة السلة عن طريق إضافتها إلى الباني constructor()‎.

  • الملف src/app/product-details/product-details.component.ts:
export class ProductDetailsComponent implements OnInit {
  constructor(
    private route: ActivatedRoute,
    private cartService: CartService
  ) { }
}

ثالثًا، عرِّف التابع addToCart()‎ الذي تضيف المنتج الحالي إلى سلة التسوق.

  • الملف src/app/product-details/product-details.component.ts:
export class ProductDetailsComponent implements OnInit {
  addToCart(product: Product) {
    this.cartService.addToCart(product);
    window.alert('Your product has been added to the cart!');
  }
}

يفعل التابع addToCart()‎ ما يلي:

  • يأخذ المنتج الحالي product على أساس وسيط.
  • يستخدِم التابع addToCart()‎ الموجود ضمن CartService لإضافة المنتج إلى السلة.
  • يعرض رسالةً تخبرنا بأنه قد أُضيف منتج إلى السلة.

رابعًا، أضف زر الشراء في ملف product-details.component.html واربط الحدث click()‎ إلى التابع addToCart()‎، إذ تحدِّث هذه الشيفرة قالب تفاصيل المنتج بإضافة زر الشراء المسؤول عن إضافة المنتج الحالي إلى سلة التسوق.

  • الملف src/app/product-details/product-details.component.html:
<h2>Product Details</h2>

<div *ngIf="product">
  <h3>{{ product.name }}</h3>
  <h4>{{ product.price | currency }}</h4>
  <p>{{ product.description }}</p>

  <button (click)="addToCart(product)">Buy</button>
</div>

خامسًا، تحقّق من ظهور زر الشراء الجديد كما هو متوقَّع من خلال تحديث التطبيق والنقر على اسم المنتج لعرض تفاصيله.

001.png

أخيرًا، انقر على زر الشراء لإضافة المنتج إلى قائمة العناصر المخزنة في سلة التسوق وعرض رسالة تأكيد.

002.png

إنشاء قسم العرض الخاص بالسلة

يمكنك إنشاء قسم العرض الخاص بسلة التسوق لكي يتمكن العملاء من رؤية سلة التسوق الخاصة بهم وذلك بخطوتين:

  • أنشئ مكوِّن السلة وهيّئ التوجيه إلى هذا المكوِّن الجديد.
  • اعرض العناصر الموجودة في السلة.

إعداد مكون السلة

اتبع الخطوات نفسها التي استخدمتها في إنشاء ProductDetailsComponent من أجل إنشاء قسم العرض الخاص بالسلة وهيِّئ التوجيه لهذا المكون الجديد.

بدايةً، أنشئ مكونًا جديدًا باسم cart يمثل السلة بتنفيذ الأمر التالي في الطرفية:

ng generate component cart

سيولد هذا الأمر الملف cart.component.ts الخاص بالمكون وقالبه وملفات تنسيقاته.

  • الملف src/app/cart/cart.component.ts:
import { Component } from '@angular/core';

@Component({
  selector: 'app-cart',
  templateUrl: './cart.component.html',
  styleUrls: ['./cart.component.css']
})
export class CartComponent {

  constructor() { }

}

ينشئ StackBlitz -الذي ضبطناه في المقال الأول ببداية المشروع- أيضًا تابع ngOnInit()‎ في المكونات افتراضيًا، ويمكنك تجاهل ngOnInit()‎ الخاصة بالمكون CartComponent في هذا المقال.

لاحظ أن المكون CartComponent المولد حديثًا يضاف إلى قسم التصريحات declarations في الملف app.module.ts.

  • الملف src/app/app.module.ts:
import { CartComponent } from './cart/cart.component';

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

أبقَ في الملف app.module.ts وأضف فيه وجهة لمكون CartComponent، مع تعيين قيمة path إلى القيمة cart.

  • الملف src/app/app.module.ts:
@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    RouterModule.forRoot([
      { path: '', component: ProductListComponent },
      { path: 'products/:productId', component: ProductDetailsComponent },
      { path: 'cart', component: CartComponent },
    ])
  ],

حدِّث زر الدفع بحيث يوجِّه إلى الرابط ‎/‎‎cart، وأضف موجِّه routerLink يشير إلى ‎/cart في ملف top-bar.component.html.

  • الملف src/app/top-bar/top-bar.component.html:
<a routerLink="/cart" class="button fancy-button">
  <i class="material-icons">shopping_cart</i>Checkout
</a>

تحقّق من عمل CartComponent كما هو متوقَّع من خلال النقر على زر الدفع، حيث يمكنك رؤية النص الافتراضي الذي وضعناه "السلة تعمل!"، ويملك الرابط النمط https://getting-started.stackblitz.io/cart، حيث أنَّ getting-started.stackblitz.io قد تكون مختلفةً في مشروع StackBlitz الخاص بك.

003.png

عرض عناصر السلة

سنعمل في هذا القسم على عرض المنتجات في السلة عن طريق استخدام خدمة السلة.

بدايةً، استورد CartService في ملف cart.component.ts من ملف cart.service.ts.

  • الملف src/app/cart/cart.component.ts:
import { Component } from '@angular/core';
import { CartService } from '../cart.service';

احقن الخدمة CartService بحيث يتمكن المكون CartComponent من استخدامه عن طريق إضافته إلى الباني constructor()‎.

  • الملف src/app/cart/cart.component.ts:
export class CartComponent {

  constructor(
    private cartService: CartService
  ) { }
}

عرِّف الخاصية items لتخزين المنتجات في السلة.

  • الملف src/app/cart/cart.component.ts:
export class CartComponent {
  items = this.cartService.getItems();

  constructor(
    private cartService: CartService
  ) { }
}

تحدد هذه الشيفرة العناصر عن طريق تابع getItems()‎ الموجودة ضمن CartService، ولقد عرَّفت هذا التابع عندما أنشأت cart.service.ts.

حدِّث قالب السلة مع إضافة ترويسة، واستخدم <div> مع ‎*ngFor لعرض كل عنصر من عناصر السلة مع اسمه وسعره، وستكون شيفرة قالب المكون CartComponent كما يلي:

  • الملف src/app/cart/cart.component.html:
<h3>Cart</h3>

<div class="cart-item" *ngFor="let item of items">
  <span>{{ item.name }}</span>
  <span>{{ item.price | currency }}</span>
</div>

تحقّق من عمل السلة كما يجب:

  • انقر على My Store.
  • انقر على اسم المنتج لعرض تفاصيله.
  • انقر زر الشراء لإضافة المنتج إلى السلة.
  • انقر فوق زر الدفع لرؤية السلة.

004.png

جلب أسعار الشحن

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

يأتي التطبيق الذي ولَّده StackBlitz مع بيانات الشحن بشكل مسبق ضمن ملف assets/shipping.json، كما يمكنك استخدام هذه البيانات لإضافة أسعار الشحن للعناصر الموجودة في السلة.

  • الملف src/assets/shipping.json:
[
  {
    "type": "Overnight",
    "price": 25.99
  },
  {
    "type": "2-Day",
    "price": 9.99
  },
  {
    "type": "Postal",
    "price": 2.99
  }
]

تهيئة AppModule لاستخدام HttpClient

يجب عليك ضبط التطبيق الخاص بك لاستخدام الوحدة HttpClientModule من أجل استخدام خدمة HttpClient من Angular، حيث يُسجِّل HttpClientModule المزوِّدين الذين يحتاجهم التطبيق الخاص بك لكي يستخدم خدمة HttpClient في جميع ملفات التطبيق.

لنبدأ، أولًا استورد الوحدة HttpClientModule في ملف app.module.ts عن طريق حزمة ‎@angular/common/http في الجزء العلوي من الملف مع باقي الاستيرادات، ونظرًا لوجود عدد من الاستيرادات الأخرى، فإن هذه الشيفرة تحذفها للإيجاز، ولذلك تأكد من وضع الاستيرادات الموجودة في مكانها الصحيح.

  • الملف src/app/app.module.ts:
import { HttpClientModule } from '@angular/common/http';

ثانيًا، لتسجيل مزوِّد HttpClient ليكون متاحًا في جميع ملفات التطبيق، أضف HttpClientModule إلى ملف AppModule ضمن مصفوفة الاستيرادات imports في ‎@NgModule.

  • الملف src/app/app.module.ts:
@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    ReactiveFormsModule,
    RouterModule.forRoot([
      { path: '', component: ProductListComponent },
      { path: 'products/:productId', component: ProductDetailsComponent },
      { path: 'cart', component: CartComponent },
    ])
  ],
  declarations: [
    AppComponent,
    TopBarComponent,
    ProductListComponent,
    ProductAlertsComponent,
    ProductDetailsComponent,
    CartComponent,
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }

تهيئة الخدمة CartService لاستخدام HttpClient

تتمثَّل الخطوة التالية في حقن خدمة HttpClient ضمن خدمتك حتى يتمكن تطبيقك من جلب البيانات والتفاعل مع الموارد وواجهات برمجة التطبيقات الخارجية.

استورد خدمة HttpClient من حزمة ‎@angular/common/http ضمن ملف cart.service.ts.

  • الملف src/app/cart.service.ts:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Product } from './products';

احقن HttpClient في الباني constructor()‎ الخاص بالخدمة CartService.

  • الملف src/app/cart.service.ts:
export class CartService {
  items: Product[] = [];

  constructor(
    private http: HttpClient
  ) {}
/* . . . */
}

تهيئة الخدمة CartService لجلب أسعار الشحن

يمكنك استخدام تابع get()‎ من HttpClient للحصول على بيانات الشحن من ملف shipping.json.

عرِّف تابعًا جديدًا getShippingPrices()‎ يستخدِم تابع get()‎ من HttpClient ضمن ملف cart.service.ts أسفل تابع clearCart()‎.

  • الملف src/app/cart.service.ts:
export class CartService {
/* . . . */
  getShippingPrices() {
    return this.http.get<{type: string, price: number}[]>('/assets/shipping.json');
  }
}

انتهينا من عملية الضبط والتهيئة لجلب أسعار الشحن.

إنشاء مكون الشحن

الآن بعدما هيّئت تطبيقك لاسترجاع بيانات الشحن، يمكنك إنشاء مكان لعرض تلك البيانات.

بدايةً، ولِّد مكوِّنًا باسم shipping يمثل عملية الشحن بتنفيذ الأمر التالي في الطرفية:

ng generate component shipping

سيولد هذا الأمر الملف shipping.component.ts الخاص بالمكون وقالبه وملفات تنسيقاته.

  • الملف src/app/shipping/shipping.component.ts:
import { Component } from '@angular/core';

@Component({
  selector: 'app-shipping',
  templateUrl: './shipping.component.html',
  styleUrls: ['./shipping.component.css']
})
export class ShippingComponent {

  constructor() { }

}

أضف مسار للشحن في ملف app.module.ts، واضبط الحقل pah إلى القيمة shipping والحقل component إلى المكون ShippingComponent.

  • الملف src/app/app.module.ts:
@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    ReactiveFormsModule,
    RouterModule.forRoot([
      { path: '', component: ProductListComponent },
      { path: 'products/:productId', component: ProductDetailsComponent },
      { path: 'cart', component: CartComponent },
      { path: 'shipping', component: ShippingComponent },
    ])
  ],
  declarations: [
    AppComponent,
    TopBarComponent,
    ProductListComponent,
    ProductAlertsComponent,
    ProductDetailsComponent,
    CartComponent,
    ShippingComponent
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }

لا يوجد رابط لمكون الشحن الجديد حتى الآن، ولكن يمكنك رؤية قالبه في جزء المعاينة عن طريق إدخال عنوان URL الذي يحدده مساره، حيث يحتوي عنوان URL على النمط الآتي: 

 https://angular-ynqttp--4200.local.webcontainer.io/shipping

حيث أنَّ جزء angular-ynqttp--4200.local.webcontainer.io قد يكون مختلفًا في مشروع StackBlitz الخاص بك.

تهيئة المكون ShippingComponent لاستخدام الخدمة CartService

ينضبط المكون ShippingComponent لجلب بيانات الشحن عبر استخدام طلب HTTP من ملف shipping.json.

أولًا، استورد CartService في ملف shipping.component.ts.

  • الملف src/app/shipping/shipping.component.ts:
import { Component } from '@angular/core';

import { CartService } from '../cart.service';

ثانيًا، احقن خدمة السلة في الباني constructor()‎ الخاص بالمكون ShippingComponent.

  • الملف src/app/shipping/shipping.component.ts:
constructor(private cartService: CartService) {
}

ثالثًا، عرِّف الخاصية shippingCosts التي تُضبط باستخدام التابع getShippingPrices()‎ من الخدمة CartService.

  • الملف src/app/shipping/shipping.component.ts:
export class ShippingComponent {
  shippingCosts = this.cartService.getShippingPrices();
}

رابعًا، حدِّث قالب المكون ShippingComponent لكي يعرض أنواع وأسعار الشحن باستخدام أنبوب async.

  • الملف src/app/shipping/shipping.component.html:
<h3>Shipping Prices</h3>

<div class="shipping-item" *ngFor="let shipping of shippingCosts | async">
  <span>{{ shipping.type }}</span>
  <span>{{ shipping.price | currency }}</span>
</div>

يُعيد أنبوب async أحدث قيمة من مجرى البيانات ويتابع تحميل مكوِّن معيَّن، حيث يتوقف أنبوب async تلقائيًا عندما تدمر Angular هذا المكوِّن.

خامسًا، أضف رابطًا من قسم العرض الخاص بالمكون CartComponent إلى قسم العرض الخاص بالمكون ShippingComponent.

  • الملف src/app/cart/cart.component.html:
<h3>Cart</h3>

<p>
  <a routerLink="/shipping">Shipping Prices</a>
</p>

<div class="cart-item" *ngFor="let item of items">
  <span>{{ item.name }}</span>
  <span>{{ item.price | currency }}</span>
</div>

سادسًا، انقر على زر الدفع Checkout لرؤية السلة المحدَّثة، وتذكَّر أنَّ أي تعديلات على التطبيق ستؤدي إلى تحديث المعاينة، وبالتالي إفراغ السلة.

005.png

انقر على الرابط للانتقال إلى أسعار الشحن.

006.png

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

تعريف نموذج استمارة الدفع

توضِّح لك هذه الخطوة كيفية إعداد نموذج استمارة الدفع في صنف المكون حيث يحدّد هذا النموذج حالة الاستمارة.

افتح الملف cart.component.ts واستورد خدمة FormBuilder من حزمة ‎@angular/forms، حيث توفِّر هذه الخدمة توابعًا ملائمةً لتوليد عناصر التحكم.

  • الملف src/app/cart/cart.component.ts:
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

import { CartService } from '../cart.service';

احقن خدمة FormBuilder في الباني constructor()‎ الخاص بالمكون CartComponent، حيث أنَّ هذه الخدمة هي جزء من وحدة ReactiveFormsModule التي استوردتها للتو.

  • الملف src/app/cart/cart.component.ts:
export class CartComponent {
  constructor(
    private cartService: CartService,
    private formBuilder: FormBuilder,
    ) {}
}

استخدم تابع group()‎ من FormBuilder من أجل الحصول على اسم المستخدِم وعنوانه وذلك من أجل ضبط خاصية checkoutForm إلى نموذج استمارة يحوي حقلَي name وaddress.

  • الملف src/app/cart/cart.component.ts:
export class CartComponent {
  items = this.cartService.getItems();
  checkoutForm = this.formBuilder.group({
    name: '',
    address: ''
  });
  constructor(
    private cartService: CartService,
    private formBuilder: FormBuilder,
    ) {}
}

عرِّف التابع onSubmit()‎ المسؤول عن معالجة الاستمارة، حيث يسمح هذا التابع للمستخدمين بإرسال أسمائهم وعناوينهم، كما أنَّ هذا التابع يستخدِم تابع clearCart()‎ الخاص بخدمة CartService لإعادة ضبط الاستمارة وتفريغ السلة، ويكون صنف المكوِّن الخاص بالسلة كاملًا كما يلي:

  • الملف src/app/cart/cart.component.ts:
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

import { CartService } from '../cart.service';

@Component({
  selector: 'app-cart',
  templateUrl: './cart.component.html',
  styleUrls: ['./cart.component.css']
})
export class CartComponent {
  items = this.cartService.getItems();
  checkoutForm = this.formBuilder.group({
    name: '',
    address: ''
  });
  constructor(
    private cartService: CartService,
    private formBuilder: FormBuilder,
    ) {}

  onSubmit(): void {
    // Process checkout data here
    this.items = this.cartService.clearCart();
    console.warn('Your order has been submitted', this.checkoutForm.value);
    this.checkoutForm.reset();
  }
}

إنشاء استمارة الدفع

اتبع الخطوات التالية لإضافة استمارة الدفع في أسفل قسم العرض الخاص بسلة التسوق.

  • أولًا، أضف وسم <form> وزر الشراء Purchase في نهاية ملف cart.component.html.
  • ثانيًا، استخدِم تربيط الخاصيات لإضافة الخاصية formGroup إلى وسم <form> وربط checkoutForm بها.
  • الملف src/app/cart/cart.component.html:
<form [formGroup]="checkoutForm">

  <button class="button" type="submit">Purchase</button>

</form>
  • ثالثًا، استخدم التربيط عن طريق الحدث ngSubmit ضمن وسم form للاستماع إلى عملية إرسال النموذج واستدعاء تابع onSubmit()‎ مع قيمة checkoutForm.

  • الملف src/app/cart/cart.component.html (تفاصيل قالب مكون السلة):

<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit()">
</form>

رابعًا، أضف حقول <input> لكل من الاسم name والعنوان address، وأضف لكل منها سمة formControlName التي تربط عناصر تحكم name وaddress الموجودة في استمارة checkoutForm بحقول <input> الخاصة بهم، ويكون المكوِّن كاملًا كما يلي:

  • الملف src/app/cart/cart.component.html:
<h3>Cart</h3>

<p>
  <a routerLink="/shipping">Shipping Prices</a>
</p>

<div class="cart-item" *ngFor="let item of items">
  <span>{{ item.name }} </span>
  <span>{{ item.price | currency }}</span>
</div>

<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit()">

  <div>
    <label for="name">
      Name
    </label>
    <input id="name" type="text" formControlName="name">
  </div>

  <div>
    <label for="address">
      Address
    </label>
    <input id="address" type="text" formControlName="address">
  </div>

  <button class="button" type="submit">Purchase</button>

</form>

يستطيع المستخدِمين بعد وضع عناصر قليلة في السلة معاينة تلك العناصر ومن ثم إدخال أسمائهم وعناوينهم وتأكيد عملية الشراء.

009.png

افتح الطرفية لتأكيد عملية الإرسال، حيث ستجد كائنًا يحتوي على الاسم والعنوان اللذَين أرسلتهما.

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

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

أكمل إلى قسم النشر لتنتقل إلى التطوير المحلي أو تنشر التطبيق الخاص بك إلى Firebase أو الخادم الخاص بك وذلك لمتابعة استكشاف Angular.

ترجمة -وبتصرف- للمقال Adding navigation والمقال Managing data والمقال Using forms for user input من موقع 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.


×
×
  • أضف...