| مستوى الحالة الشرطية | التعبير البرمجي المرتبط | الأثر الهندسي على تجربة المستخدم |
|---|---|---|
| الحقل غير صالح ولم يُلمس بعد | `control.invalid && control.touched` | إخفاء رسائل الخطأ لمنع إزعاج المستخدم قبل بدء الكتابة الفعلية |
| الحقل خطأ وتم تلمسه ومغادرته | `control.invalid && (control.touched || control.dirty)` | إظهار رسالة الخطأ بوضوح لتوجيه المستخدم لتصحيح المدخلات |
| النموذج ككل جاهز للإرسال | `form.valid` | تفعيل زر الحفظ والإرسال نهائياً لمنع إرسال بيانات ناقصة |
يُعتبر التعامل الاحترافي مع عرض رسائل الخطأ والتفاعل مع حالات الحقول مظهراً أساسياً لبناء واجهات مستخدم (UX) نظيفة وواضحة. من خلال الدمج بين مؤشرات التتبع (touched, dirty, invalid) ومتغيرات القالب المحلية، يمكنك إظهار رسائل التحذير بدقة متناهية وفقط عند استيفاء الشروط المعمارية المناسبة.
لضمان معمارية نظيفة، يجب ألا تظهر أخطاء التحقق بمجرد تحميل المكون، بل يجب دمج حالة الخطأ (invalid) مع مؤشر التفاعل البشري (touched أو dirty) لضمان ظهور الخطأ في الوقت الهندسي المناسب.
يوفر أنجولر كائناً فرعياً لكل حقل (control.errors) يحمل تفاصيل قواعد التحقق التي فشلت (مثل required, minlength, email), مما يتيح لك تخصيص رسالة دقيقة لكل حالة على حدة.
| 1 | import { Component } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { FormsModule } from '@angular/forms'; |
| 4 | |
| 5 | @Component({ |
| 6 | selector: 'app-error-handling-demo', |
| 7 | standalone: true, |
| 8 | imports: [CommonModule, FormsModule], |
| 9 | template: ` |
| 10 | <div class="error-demo-card"> |
| 11 | <h3>نموذج معالجة الأخطاء والتفاعل المتقدم</h3> |
| 12 | |
| 13 | <form #demoForm="ngForm" (ngSubmit)="submitForm(demoForm)"> |
| 14 | |
| 15 | <div class="field-container"> |
| 16 | <label for="username">اسم المستخدم:</label> |
| 17 | <input |
| 18 | type="text" |
| 19 | id="username" |
| 20 | name="username" |
| 21 | [(ngModel)]="username" |
| 22 | required |
| 23 | minlength="4" |
| 24 | #userRef="ngModel" |
| 25 | class="form-input" |
| 26 | [class.input-error]="userRef.invalid && userRef.touched" |
| 27 | /> |
| 28 | |
| 29 | <div *ngIf="userRef.invalid && userRef.touched" class="error-msg-box"> |
| 30 | <span *ngIf="userRef.errors?.['required']">اسم المستخدم حقل إجباري لا يمكن تركه فارغاً.</span> |
| 31 | <span *ngIf="userRef.errors?.['minlength']">يجب ألا يقل اسم المستخدم عن 4 أحرف هجائية.</span> |
| 32 | </div> |
| 33 | </div> |
| 34 | |
| 35 | <button type="submit" [disabled]="demoForm.invalid" class="submit-action-btn"> |
| 36 | إرسال البيانات برمجياً |
| 37 | </button> |
| 38 | </form> |
| 39 | </div> |
| 40 | `, |
| 41 | styles: [` |
| 42 | .error-demo-card { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 43 | .field-container { margin-bottom: 18px; display: flex; flex-direction: column; } |
| 44 | .form-input { background: #1e293b; border: 1px solid #334155; padding: 10px; border-radius: 6px; color: #fff; margin-top: 6px; } |
| 45 | .input-error { border-color: #f43f5e !important; } |
| 46 | .error-msg-box { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; color: #f43f5e; font-size: 12px; font-weight: bold; } |
| 47 | .submit-action-btn { background: #10b981; color: #090d16; border: none; padding: 10px 20px; border-radius: 6px; font-weight: bold; cursor: pointer; width: 100%; } |
| 48 | .submit-action-btn:disabled { background: #475569; cursor: not-allowed; color: #94a3b8; } |
| 49 | `] |
| 50 | }) |
| 51 | export class ErrorHandlingDemoComponent { |
| 52 | username = ''; |
| 53 | |
| 54 | submitForm(form: any) { |
| 55 | if (form.valid) { |
| 56 | console.log('النموذج صالح وتم إرساله بنجاح:', form.value); |
| 57 | } |
| 58 | } |
| 59 | } |
⚠️ تحذير
تجنب إظهار الأخطاء قبل تلمس الحقل! إظهار رسائل الخطأ والحقول ملونة باللون الأحمر فور فتح الصفحة وقبل أن يفعل المستخدم أي شيء يُعتبر أسوأ ممارسة في هندسة تجربة المستخدم (UX).
| 1 | import { Component } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { FormsModule } from '@angular/forms'; |
| 4 | |
| 5 | @Component({ |
| 6 | selector: 'app-form-status-dashboard', |
| 7 | standalone: true, |
| 8 | imports: [CommonModule, FormsModule], |
| 9 | template: ` |
| 10 | <div class="form-card-demo"> |
| 11 | <h3>لوحة تتبع حالة النموذج الكلي</h3> |
| 12 | |
| 13 | <form #statusForm="ngForm" class="dashboard-form"> |
| 14 | <div class="control-group"> |
| 15 | <input |
| 16 | type="text" |
| 17 | name="projectTitle" |
| 18 | [(ngModel)]="projectTitle" |
| 19 | required |
| 20 | placeholder="اسم المشروع الهيكلي..." |
| 21 | class="input-field" |
| 22 | /> |
| 23 | </div> |
| 24 | |
| 25 | <!-- مؤشرات حالة النموذج الكلي --> |
| 26 | <div class="status-grid"> |
| 27 | <div class="status-badge" [class.valid-badge]="statusForm.valid" [class.invalid-badge]="statusForm.invalid"> |
| 28 | حالة النموذج الكلي: {{ statusForm.valid ? 'صالح للإرسال' : 'غير صالح' }} |
| 29 | </div> |
| 30 | <div class="status-badge" [class.active-badge]="statusForm.dirty"> |
| 31 | هل تم تعديل البيانات؟: {{ statusForm.dirty ? 'نعم' : 'لا' }} |
| 32 | </div> |
| 33 | </div> |
| 34 | </form> |
| 35 | </div> |
| 36 | `, |
| 37 | styles: [` |
| 38 | .form-card-demo { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 39 | .control-group { margin-bottom: 16px; } |
| 40 | .input-field { background: #1e293b; border: 1px solid #334155; padding: 10px; border-radius: 6px; color: #fff; width: 100%; box-sizing: border-box; } |
| 41 | .status-grid { display: flex; gap: 12px; margin-top: 16px; flex-wrap: wrap; } |
| 42 | .status-badge { background: #1e293b; padding: 10px 14px; border-radius: 8px; border: 1px solid #334155; font-size: 13px; font-weight: bold; color: #94a3b8; } |
| 43 | .valid-badge { color: #10b981; border-color: #10b981; } |
| 44 | .invalid-badge { color: #f43f5e; border-color: #f43f5e; } |
| 45 | .active-badge { color: #38bdf8; border-color: #38bdf8; } |
| 46 | `] |
| 47 | }) |
| 48 | export class FormStatusDashboardComponent { |
| 49 | projectTitle = ''; |
| 50 | } |