| جانب المقارنة المعمارية | أدوات التحقق المدمجة (`Built-in Validators`) | أدوات التحقق المخصصة (`Custom Validators`) |
|---|---|---|
| نطاق الاستخدام | تغطية القواعد العامة الشائعة مثل الحقول الإجباريّة والأطوال المحددة | تغطية شروط العمل المعقدة والفريدة الخاصة بالنظام التجاري للشركة |
| مرونة البرمجة | ثابتة ومكتوبة مسبقاً داخل إطار أنگولر ولا يمكن تعديل سلوكها الداخلي | دوال برمجية مرنة تقبل معاملات إضافية وتفحص أي منطق رياضي أو نصي معقد |
| طريقة التنفيذ | تُستدعى عبر السمات المباشرة مثل `required` أو `minlength` | تُمرر عبر مصفوفة أدوات التحقق البرمجية داخل النماذج التفاعلية أو التوجيهات |
يأتي وقت تتجاوز فيه النماذج قواعد التحقق الجاهزة (Built-in Validators) لتلبي احتياجات العمل المعقدة. يُعتبر بناء مؤشرات ومتحكمات مخصصة للتحقق من صحة المدخلات (Building Custom Validators) مهارة هندسية رفيعة تسمح لك بكتابة دالة تحقق برمجية مستقلة (ValidatorFn) تفحص القيم وتعيد كائن خطأ مخصص أو قيمة null عند الصلاحية التامة، مما يضمن مرونة مطلقة في إدارة قواعد البيانات الواردة.
هي دالة برمجية تستقبل مرجع الحقل (AbstractControl) وتعيد إما كائن يحمل اسم الخطأ مع قيمة منطقية (مثل { customError: true }) إذا فشل التحقق، أو تُعيد null إذا كانت البيانات سليمة ومستوفية للشروط.
يمكنك إنشاء مصنع دوال تحقق مخصص (Validator Factory) يتقبل متغيرات خارجية (مثل الحد الأقصى المخصص للقيمة) ويُعيد دالة التحقق الفعلية ليتم تطبيقها بكفاءة عالية على الحقول.
| 1 | import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; |
| 2 | |
| 3 | // دالة تحقق مخصصة تتأكد أن النص المدخل لا يحتوي على كلمات محظورة |
| 4 | export function forbiddenNameValidator(forbiddenName: RegExp): ValidatorFn { |
| 5 | return (control: AbstractControl): ValidationErrors | null => { |
| 6 | const value = control.value; |
| 7 | if (!value) { |
| 8 | return null; |
| 9 | } |
| 10 | const isForbidden = forbiddenName.test(value); |
| 11 | // إرجاع كائن خطأ مخصص إذا تطابق النص مع المحظورات، وإلا إرجاع null |
| 12 | return isForbidden ? { forbiddenName: { value: value } } : null; |
| 13 | }; |
| 14 | } |
| 1 | import { Component } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; |
| 4 | import { forbiddenNameValidator } from './forbidden-name.validator'; |
| 5 | |
| 6 | @Component({ |
| 7 | selector: 'app-custom-validation-demo', |
| 8 | standalone: true, |
| 9 | imports: [CommonModule, ReactiveFormsModule], |
| 10 | template: ` |
| 11 | <div class="custom-val-card"> |
| 12 | <h3>نموذج التحقق المخصص وتطبيق القواعد الهندسية</h3> |
| 13 | |
| 14 | <form [formGroup]="customForm" (ngSubmit)="onSubmit()"> |
| 15 | |
| 16 | <div class="input-block-wrap"> |
| 17 | <label for="username">اسم المستخدم المعماري:</label> |
| 18 | <input |
| 19 | type="text" |
| 20 | id="username" |
| 21 | formControlName="username" |
| 22 | class="styled-control-input" |
| 23 | [class.error-highlight]="customForm.get('username')?.invalid && customForm.get('username')?.touched" |
| 24 | /> |
| 25 | |
| 26 | <div *ngIf="customForm.get('username')?.invalid && customForm.get('username')?.touched" class="error-msg-box"> |
| 27 | <span *ngIf="customForm.get('username')?.errors?.['required']">اسم المستخدم حقل إجباري.</span> |
| 28 | <span *ngIf="customForm.get('username')?.errors?.['forbiddenName']">هذا الاسم محظور برمجياً ولا يمكن استخدامه.</span> |
| 29 | </div> |
| 30 | </div> |
| 31 | |
| 32 | <button type="submit" [disabled]="customForm.invalid" class="submit-action-btn"> |
| 33 | إرسال النموذج المُحقق |
| 34 | </button> |
| 35 | </form> |
| 36 | </div> |
| 37 | `, |
| 38 | styles: [` |
| 39 | .custom-val-card { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 40 | .input-block-wrap { margin-bottom: 18px; display: flex; flex-direction: column; } |
| 41 | .styled-control-input { background: #1e293b; border: 1px solid #334155; padding: 10px; border-radius: 6px; color: #fff; margin-top: 6px; } |
| 42 | .error-highlight { border-color: #f43f5e !important; } |
| 43 | .error-msg-box { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; color: #f43f5e; font-size: 12px; font-weight: bold; } |
| 44 | .submit-action-btn { background: #10b981; color: #090d16; border: none; padding: 10px 20px; border-radius: 6px; font-weight: bold; cursor: pointer; width: 100%; } |
| 45 | .submit-action-btn:disabled { background: #475569; cursor: not-allowed; color: #94a3b8; } |
| 46 | `] |
| 47 | }) |
| 48 | export class CustomValidationDemoComponent { |
| 49 | customForm: FormGroup; |
| 50 | |
| 51 | constructor(private fb: FormBuilder) { |
| 52 | this.customForm = this.fb.group({ |
| 53 | // تطبيق أداة التحقق المخصصة وحظر كلمات مثل admin أو root |
| 54 | username: ['', [Validators.required, forbiddenNameValidator(/admin|root/i)]] |
| 55 | }); |
| 56 | } |
| 57 | |
| 58 | onSubmit() { |
| 59 | if (this.customForm.valid) { |
| 60 | console.log('النموذج اجتاز التحقق المخصص بنجاح:', this.customForm.value); |
| 61 | } |
| 62 | } |
| 63 | } |
⚠️ تحذير
لا تقم بتعديل حالة الحقول الأخرى من داخل دالة التحقق المخصصة! دالة التحقق مهمتها الوحيدة هي الفحص وإعادة نتيجة الصلاحية؛ محاولة تعديل قيمة أو حالة حقل آخر من داخلها ستؤدي إلى تداخلات برمجية خطيرة في حلقة التحديث وحالة النموذج