| نوع الحدث (`HttpEventType`) | معناه ومرحلة التنفيذ الشبكي | طريقة الاستفادة منه في الواجهة |
|---|---|---|
| `HttpEventType.Sent` | تم إرسال الطلب الفعلي وبدء الاتصال بالخادم | إعلام المستخدم ببدء العملية |
| `HttpEventType.UploadProgress` | جارٍ رفع الملف ويحتوي على نسبتي `loaded` و `total` | حساب النسبة المئوية وتحديث شريط التقدم المرئي (`Progress Bar`) |
| `HttpEventType.Response` | تم استلام الاستجابة النهائية بنجاح من الخادم | إخفاء شريط التقدم وعرض رسالة النجاح النهائية |
عند التعامل مع ملفات ضخمة أو رفع مستندات برمجية ثقيلة، يحتاج المستخدمون إلى رؤية مؤشر تقدم مرئي (Progress Bar) لضمان عدم تجمد الشاشة. يتيح لك نظام HttpClient في أنجولر تحويل الطلب ليتتبع كافة أحداث التقدم اللحظية عبر استخدام خيار reportProgress: true وتتبع أنواع الأحداث (HttpEventType).
يتيح تفعيل خيار تتبع التقدم تحويل الطلب العادي إلى سلسلة أحداث زمنية متتالية تعكس حجم البيانات المنقولة لحظة بلحظة.
من خلال مقارنة الحجم المُرسل (loaded) بالحجم الكلي (file.size)، يمكن تحديث واجهة المستخدم بصرياً بسلاسة تامة.
| 1 | import { Component, inject } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { HttpClient, HttpEventType } from '@angular/common/http'; |
| 4 | |
| 5 | @Component({ |
| 6 | selector: 'app-file-uploader-progress', |
| 7 | standalone: true, |
| 8 | imports: [CommonModule], |
| 9 | template: ` |
| 10 | <div class="api-card"> |
| 11 | <h3>تتبع تقدم عملية رفع الملفات</h3> |
| 12 | <input type="file" (change)="onFileSelected($event)" class="file-input" /> |
| 13 | <div class="progress-bar-container" *ngIf="uploadProgress !== null"> |
| 14 | <div class="progress-fill" [style.width.%]="uploadProgress"></div> |
| 15 | </div> |
| 16 | <p><strong>نسبة الرفع الحالية:</strong> {{ uploadProgress !== null ? uploadProgress + '%' : 'لم يبدأ الرفع' }}</p> |
| 17 | </div> |
| 18 | `, |
| 19 | styles: [` |
| 20 | .api-card { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 21 | .file-input { margin-bottom: 16px; color: #94a3b8; } |
| 22 | .progress-bar-container { width: 100%; background: #1e293b; height: 12px; border-radius: 6px; overflow: hidden; margin-bottom: 12px; } |
| 23 | .progress-fill { background: #38bdf8; height: 100%; transition: width 0.2s ease; } |
| 24 | p { color: #94a3b8; } |
| 25 | strong { color: #10b981; } |
| 26 | `] |
| 27 | }) |
| 28 | export class FileUploaderProgressComponent { |
| 29 | uploadProgress: number | null = null; |
| 30 | private http = inject(HttpClient); |
| 31 | |
| 32 | onFileSelected(event: any): void { |
| 33 | const file: File = event.target.files[0]; |
| 34 | if (!file) return; |
| 35 | |
| 36 | const formData = new FormData(); |
| 37 | formData.append('file', file); |
| 38 | |
| 39 | // إرسال الطلب مع تفعيل تتبع التقدم |
| 40 | const req = this.http.post('https://jsonplaceholder.typicode.com/posts', formData, { |
| 41 | reportProgress: true, |
| 42 | observe: 'events' |
| 43 | }); |
| 44 | |
| 45 | req.subscribe({ |
| 46 | next: (event: any) => { |
| 47 | if (event.type === HttpEventType.UploadProgress) { |
| 48 | if (event.total) { |
| 49 | this.uploadProgress = Math.round(100 * (event.loaded / event.total)); |
| 50 | console.log(`نسبة الرفع: ${this.uploadProgress}%`); |
| 51 | } |
| 52 | } else if (event.type === HttpEventType.Response) { |
| 53 | console.log('تم رفع الملف بنجاح تام وتلقي الاستجابة:', event.body); |
| 54 | } |
| 55 | }, |
| 56 | error: (err) => { |
| 57 | console.error('فشل عملية الرفع:', err); |
| 58 | this.uploadProgress = null; |
| 59 | } |
| 60 | }); |
| 61 | } |
| 62 | } |