| نوع مدقق الصحة (`Validation Type`) | طريقة العمل البرمجي | حالة الاستخدام المعمارية النموذجية |
|---|---|---|
| المدقق المدمج (`Built-in Validators`) | استخدام الدوال الجاهزة مثل `Validators.required` و `Validators.email` | التحقق السريع من الشروط الأساسية المباشرة لكل حقل منفرد |
| مدقق الحقول المخصص (`Custom Sync Validator`) | دالة برمجية مستقلة تعيد كائن أخطاء `ValidationErrors | null` | تنفيذ قواعد عمل دقيقة تخص النظام (مثل مطابقة كلمة المرور مع تأكيدها) |
| المدقق غير المتزامن (`Async Validator`) | إجراء اتصال برمجي أو طلب خارجي (`Observable`) للتحقق من الحالة | التحقق من توفر البريد الإلكتروني أو اسم المستخدم في قاعدة البيانات لحظياً |
لا يقتصر التحقق من صحة البيانات على القواعد البسيطة مثل الحقول الفارغة أو الطول الأدنى، بل يمتد ليشمل التحقق المشترك بين عدة حقول (Cross-Field Validation)، والتحقق المتزامن وغير المتزامن (Async Validators) مثل فحص توفر اسم المستخدم في الخادم لحظياً. يضمن هذا النهج الهندسي بناء نماذج قوية ومحمية ضد الأخطاء البرمجية ومنطق الأعمال الخاطئ.
عندما تتعلق قاعدة الصحة بحقلين معاً (مثل تطابق كلمة المرور وتأكيدها)، يتم تطبيق المدقق على مستوى الـ FormGroup بالكامل وليس على حقل منفرد، مما يتيح الوصول لجميع قيم الحقول المرتبطة في نفس السياق.
فصل منطق التحقق في دوال مستقلة يجعل الكود قابلاً للاختبار (Unit Testing) ويسهل إعادة استخدامه في أماكن متعددة داخل التطبيق.
| 1 | import { Component, OnInit } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { ReactiveFormsModule, FormBuilder, FormGroup, Validators, AbstractControl, ValidationErrors, ValidatorFn } from '@angular/core'; |
| 4 | |
| 5 | // دالة مخصصة للتحقق من تطابق كلمتي المرور |
| 6 | export const passwordMatchValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { |
| 7 | const password = control.get('password'); |
| 8 | const confirmPassword = control.get('confirmPassword'); |
| 9 | |
| 10 | if (!password || !confirmPassword) { |
| 11 | return null; |
| 12 | } |
| 13 | |
| 14 | return password.value === confirmPassword.value ? null : { passwordMismatch: true }; |
| 15 | }; |
| 16 | |
| 17 | @Component({ |
| 18 | selector: 'app-advanced-validation', |
| 19 | standalone: true, |
| 20 | imports: [CommonModule, ReactiveFormsModule], |
| 21 | template: ` |
| 22 | <div class="advanced-validation-card"> |
| 23 | <h3>التحقق المتقدم وقواعد العمل المشتركة</h3> |
| 24 | |
| 25 | <form [formGroup]="registrationForm" (ngSubmit)="onAdvancedSubmit()"> |
| 26 | |
| 27 | <!-- البريد الإلكتروني --> |
| 28 | <div class="field-block"> |
| 29 | <label>البريد الإلكتروني:</label> |
| 30 | <input type="email" formControlName="email" class="styled-input" /> |
| 31 | <div *ngIf="registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched" class="error-text"> |
| 32 | البريد الإلكتروني مطلوب وغير صالح. |
| 33 | </div> |
| 34 | </div> |
| 35 | |
| 36 | <!-- مجموعة كلمات المرور الخاضعة للتدقيق المتبادل --> |
| 37 | <div formGroupName="passwordsGroup" class="sub-group-container" [class.error-box]="registrationForm.get('passwordsGroup')?.errors?.['passwordMismatch'] && registrationForm.get('passwordsGroup')?.touched"> |
| 38 | <h4>إعدادات الأمان وتأكيد كلمة المرور</h4> |
| 39 | |
| 40 | <div class="field-block"> |
| 41 | <input type="password" formControlName="password" placeholder="كلمة المرور..." class="styled-input" /> |
| 42 | </div> |
| 43 | |
| 44 | <div class="field-block"> |
| 45 | <input type="password" formControlName="confirmPassword" placeholder="تأكيد كلمة المرور..." class="styled-input" /> |
| 46 | </div> |
| 47 | |
| 48 | <!-- رسالة خطأ التطابق على مستوى المجموعة --> |
| 49 | <div *ngIf="registrationForm.get('passwordsGroup')?.errors?.['passwordMismatch'] && registrationForm.get('passwordsGroup')?.touched" class="error-text"> |
| 50 | كلمتا المرور غير متطابقتين، يرجى التحقق مرة أخرى. |
| 51 | </div> |
| 52 | </div> |
| 53 | |
| 54 | <!-- زر الإرسال --> |
| 55 | <button type="submit" [disabled]="registrationForm.invalid" class="submit-action-btn"> |
| 56 | إرسال النموذج المُدقق برمجياً |
| 57 | </button> |
| 58 | </form> |
| 59 | </div> |
| 60 | `, |
| 61 | styles: [` |
| 62 | .advanced-validation-card { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 63 | .field-block { margin-bottom: 14px; display: flex; flex-direction: column; gap: 4px; } |
| 64 | .styled-input { background: #1e293b; border: 1px solid #334155; padding: 10px; border-radius: 6px; color: #fff; } |
| 65 | .sub-group-container { background: #1e293b; border: 1px solid #334155; padding: 16px; border-radius: 8px; margin-bottom: 16px; } |
| 66 | .sub-group-container h4 { margin-top: 0; color: #38bdf8; font-size: 14px; margin-bottom: 12px; } |
| 67 | .error-box { border-color: #f43f5e !important; } |
| 68 | .error-text { color: #f43f5e; font-size: 12px; font-weight: bold; } |
| 69 | .submit-action-btn { background: #10b981; color: #090d16; border: none; padding: 12px 20px; border-radius: 6px; font-weight: bold; cursor: pointer; width: 100%; } |
| 70 | .submit-action-btn:disabled { background: #475569; cursor: not-allowed; color: #94a3b8; } |
| 71 | `] |
| 72 | }) |
| 73 | export class AdvancedValidationComponent implements OnInit { |
| 74 | registrationForm!: FormGroup; |
| 75 | |
| 76 | constructor(private fb: FormBuilder) {} |
| 77 | |
| 78 | ngOnInit(): void { |
| 79 | // بناء الهيكل مع ربط المدقق المخصص على مستوى مجموعة كلمات المرور |
| 80 | this.registrationForm = this.fb.group({ |
| 81 | email: ['', [Validators.required, Validators.email]], |
| 82 | passwordsGroup: this.fb.group({ |
| 83 | password: ['', [Validators.required, Validators.minLength(6)]], |
| 84 | confirmPassword: ['', [Validators.required]] |
| 85 | }, { validators: passwordMatchValidator }) |
| 86 | }); |
| 87 | } |
| 88 | |
| 89 | onAdvancedSubmit(): void { |
| 90 | if (this.registrationForm.valid) { |
| 91 | console.log('البيانات المُعتمدة بعد اجتياز التحقق المتقدم:', this.registrationForm.value); |
| 92 | } |
| 93 | } |
| 94 | } |