| مستوى المقارنة المعمارية | نماذج القوالب (`Template-Driven Forms`) | النماذج التفاعلية (`Reactive Forms`) |
|---|---|---|
| مكان تعريف الحالة والبنية | في قالب الـ HTML (بشكل خفي عبر توجيهات NgModel) | صريح ومباشر داخل صنف الـ TypeScript البرمجي |
| إمكانية الاختبار والصيانة | أقل مرونة وصعوبة في الاختبار الآلي بسبب الاعتماد على القالب | عالية جداً وسهلة الاختبار نظراً لكونها كائنات برمجية بحتة |
| التفاعل المتقدم مع التدفقات | محدودة وتتطلب حلولاً إضافية لمراقبة التغيير | قوية للغاية بفضل الدعم المباشر لتدفقات RxJS مثل `valueChanges` |
تُعتبر النماذج التفاعلية (Reactive Forms) الأداة الأساسية لبناء أنظمة إدخال بيانات معقدة وقابلة للاختبار (Testable). تعتمد هذه الهندسة على إدارة حالة النموذج برمجياً داخل صنف الـ TypeScript بأسلوب تفاعلي وتصريحي (Declarative), مما يمنح المطور سيطرة كاملة على تتبع التغييرات، وتطبيق قواعد التحقق الديناميكية، والتكامل السلس مع تدفقات بيانات RxJS.
يتم تمثيل كل حقل عبر كائن FormControl، والمجموعات عبر FormGroup, مما يتيح لك تعديل القيم، أو تجميد الحقول (disable), أو تغيير قواعد التحقق (setValidators) برمجياً في أي لحظة دون التدخل في قالب العرض.
تتيح لك النماذج التفاعلية مراقبة القيم والمتغيرات لحظياً عبر خصائص مثل valueChanges و statusChanges، مما يسهل تنفيذ عمليات البحث التلقائي (Live Search) أو التحقق المتبادل بين الحقول بكفاءة استثنائية.
| 1 | import { Component, OnInit } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; |
| 4 | |
| 5 | @Component({ |
| 6 | selector: 'app-reactive-form-demo', |
| 7 | standalone: true, |
| 8 | imports: [CommonModule, ReactiveFormsModule], |
| 9 | template: ` |
| 10 | <div class="reactive-card-demo"> |
| 11 | <h3>هندسة النماذج التفاعلية المتقدمة</h3> |
| 12 | |
| 13 | <form [formGroup]="userProfileForm" (ngSubmit)="onReactiveSubmit()"> |
| 14 | |
| 15 | <!-- حقل اسم المستخدم --> |
| 16 | <div class="field-block"> |
| 17 | <label for="userName">اسم المستخدم البرمجي:</label> |
| 18 | <input |
| 19 | type="text" |
| 20 | id="userName" |
| 21 | formControlName="userName" |
| 22 | class="styled-input-field" |
| 23 | [class.error-border]="userProfileForm.get('userName')?.invalid && userProfileForm.get('userName')?.touched" |
| 24 | /> |
| 25 | <div *ngIf="userProfileForm.get('userName')?.invalid && userProfileForm.get('userName')?.touched" class="error-msg"> |
| 26 | اسم المستخدم مطلوب ويجب ألا يقل عن 3 أحرف. |
| 27 | </div> |
| 28 | </div> |
| 29 | |
| 30 | <!-- حقل البريد الإلكتروني --> |
| 31 | <div class="field-block"> |
| 32 | <label for="userEmail">البريد الإلكتروني:</label> |
| 33 | <input |
| 34 | type="email" |
| 35 | id="userEmail" |
| 36 | formControlName="userEmail" |
| 37 | class="styled-input-field" |
| 38 | [class.error-border]="userProfileForm.get('userEmail')?.invalid && userProfileForm.get('userEmail')?.touched" |
| 39 | /> |
| 40 | <div *ngIf="userProfileForm.get('userEmail')?.invalid && userProfileForm.get('userEmail')?.touched" class="error-msg"> |
| 41 | يرجى إدخال بريد إلكتروني صالح. |
| 42 | </div> |
| 43 | </div> |
| 44 | |
| 45 | <!-- زر الإرسال المشروط --> |
| 46 | <button type="submit" [disabled]="userProfileForm.invalid" class="submit-action-btn"> |
| 47 | حفظ وإرسال النموذج التفاعلي |
| 48 | </button> |
| 49 | </form> |
| 50 | </div> |
| 51 | `, |
| 52 | styles: [` |
| 53 | .reactive-card-demo { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 54 | .field-block { margin-bottom: 18px; display: flex; flex-direction: column; } |
| 55 | .styled-input-field { background: #1e293b; border: 1px solid #334155; padding: 10px; border-radius: 6px; color: #fff; margin-top: 6px; } |
| 56 | .error-border { border-color: #f43f5e !important; } |
| 57 | .error-msg { color: #f43f5e; font-size: 12px; margin-top: 4px; font-weight: bold; } |
| 58 | .submit-action-btn { background: #10b981; color: #090d16; border: none; padding: 10px 20px; border-radius: 6px; font-weight: bold; cursor: pointer; width: 100%; } |
| 59 | .submit-action-btn:disabled { background: #475569; cursor: not-allowed; color: #94a3b8; } |
| 60 | `] |
| 61 | }) |
| 62 | export class ReactiveFormDemoComponent implements OnInit { |
| 63 | userProfileForm!: FormGroup; |
| 64 | |
| 65 | constructor(private fb: FormBuilder) {} |
| 66 | |
| 67 | ngOnInit(): void { |
| 68 | // بناء هيكل النموذج التفاعلي باستخدام FormBuilder لتنظيم الكود |
| 69 | this.userProfileForm = this.fb.group({ |
| 70 | userName: ['', [Validators.required, Validators.minLength(3)]], |
| 71 | userEmail: ['', [Validators.required, Validators.email]] |
| 72 | }); |
| 73 | |
| 74 | // مثال على مراقبة التغييرات برمجياً باستخدام تدفقات RxJS |
| 75 | this.userProfileForm.get('userName')?.valueChanges.subscribe(value => { |
| 76 | console.log('قيمة اسم المستخدم تتغير لحظياً:', value); |
| 77 | }); |
| 78 | } |
| 79 | |
| 80 | onReactiveSubmit() { |
| 81 | if (this.userProfileForm.valid) { |
| 82 | console.log('بيانات النموذج التفاعلي المُعتمدة:', this.userProfileForm.value); |
| 83 | } |
| 84 | } |
| 85 | } |