فرم و اعتبارسنجی

تمام فرم‌ها با پیام‌های فارسی، اعتبارسنجی زنده و هفت حالت بصری آماده‌اند. هیچ کتابخانه اعتبارسنجی نصب نمی‌شود.

حالت‌های فرم

حالتکلاس / وضعیت
پیش‌فرض.form-control
فوکوسخودکار توسط CSS (:focus-visible)
پرشده.is-filled (با JS بعد از ورود مقدار)
غیرفعالdisabled
فقط‌خواندنیreadonly
خطا.is-invalid + .field-feedback
موفق.is-valid
در حال ارسال.is-loading روی دکمه ثبت

اعتبارسنجی اعلانی

<form data-validate novalidate>
  <div class="form-field">
    <label class="form-label" for="email">ایمیل</label>
    <input id="email" name="email" class="form-control" type="email" required data-rule="email" data-validate-live="true">
    <p class="field-feedback" hidden></p>
  </div>

  <div class="form-field">
    <label class="form-label" for="nationalId">کد ملی</label>
    <input id="nationalId" name="nationalId" class="form-control" data-rule="nationalId">
  </div>

  <div class="form-field">
    <label class="form-label" for="password">گذرواژه</label>
    <input id="password" name="password" class="form-control" type="password" required data-rule="password" data-min="8" data-password-field="#password-meter">
    <div class="password-meter" id="password-meter"><span data-password-bar></span><span data-password-bar></span><span data-password-bar></span><span data-password-bar></span><small data-password-label></small></div>
  </div>

  <div class="form-field">
    <label class="form-label" for="confirm">تکرار گذرواژه</label>
    <input id="confirm" name="confirm" class="form-control" type="password" required data-match="password">
  </div>

  <button class="btn btn-primary" type="submit">ذخیره</button>
</form>

قواعد آماده

قاعدهبررسی
requiredخالی نبودن
emailقالب ایمیل
phoneشماره موبایل ایران (۰۹xxxxxxxxx)
nationalIdکد ملی با رقم کنترلی
postalکد پستی ۱۰ رقمی
ibanشبا با پیشوند IR
urlنشانی وب
numberعدد (با پشتیبانی ارقام فارسی)
passwordحداقل طول (data-min)
patternالگوی data-pattern
min / maxطول مقدار
matchبرابری با فیلد دیگر
termsپذیرش قوانین

ارقام فارسی و عربی پیش از بررسی به لاتین تبدیل می‌شوند؛ کاربر می‌تواند «۰۹۱۲۳۴۵۶۷۸۹» یا «09123456789» بنویسد.

اعتبارسنجی برنامه‌ای

import { validateForm, validateField } from './js/core/form.js';

const { valid, fields } = validateForm(form);
if (!valid) {
  console.log(fields.filter((item) => !item.valid).map((item) => item.field.name));
}

ویجت‌های فرم

ویجتنشانه در HTML
ناحیه کشیدن فایل[data-file-drop]
ورودی برچسب‌دار[data-tag-input]
نوار قدرت گذرواژه[data-password-field="#meter"] + [data-password-bar]
انتخاب وابسته[data-depends-on="province"]
شمارنده[data-stepper]
کد یک‌بار‌مصرف[data-otp] با .otp-row و .otp-input
برنامه‌ریز تاریخinput[type="date"] / [data-datepicker]
ورودی مبلغ با ماسک هزارگان[data-currency-input]

همه این‌ها با initForms() در main.js راه‌اندازی می‌شوند و روی محتوای تازه‌رندرشده هم با initForms(scope) قابل اعمال‌اند.

فرم‌های پویا

برای ساختن فرم از روی تعریف (مثل فرم‌های «افزودن رکورد»):

import { formMarkup, collectValues, openRecordForm } from './js/pages/kit.js';

const markup = formMarkup([
  { name: 'title', label: 'عنوان', required: true },
  { name: 'price', label: 'قیمت', type: 'number', rule: 'number' },
  { name: 'status', label: 'وضعیت', type: 'select', options: ['active', 'draft'] },
  { name: 'notes', label: 'توضیحات', type: 'textarea', col: 2, rows: 4 },
]);
نکته: دکمه ثبت را با کلاس is-loading علامت بزنید تا در طول درخواست غیرفعال و اسپینری شود؛ در همه فرم‌های قالب همین الگو استفاده شده است
هشدار: هرگز اعتبارسنجی سمت کلاینت را جای اعتبارسنجی سرور نگذارید

این لایه برای تجربه کاربری است، نه امنیت.

Forms and validation

All forms come ready with localized messages, live validation and several visual states. No validation library is installed.

Form states

StateClass / attribute
Default.form-control
FocusAutomatic via CSS (:focus-visible)
Filled.is-filled (set by JS after a value is entered)
Disableddisabled
Read-onlyreadonly
Error.is-invalid + .field-feedback
Success.is-valid
Submitting.is-loading on the submit button

Declarative validation

<form data-validate novalidate>
  <div class="form-field">
    <label class="form-label" for="email">Email</label>
    <input id="email" name="email" class="form-control" type="email" required data-rule="email" data-validate-live="true">
    <p class="field-feedback" hidden></p>
  </div>

  <div class="form-field">
    <label class="form-label" for="nationalId">National ID</label>
    <input id="nationalId" name="nationalId" class="form-control" data-rule="nationalId">
  </div>

  <div class="form-field">
    <label class="form-label" for="password">Password</label>
    <input id="password" name="password" class="form-control" type="password" required data-rule="password" data-min="8" data-password-field="#password-meter">
    <div class="password-meter" id="password-meter"><span data-password-bar></span><span data-password-bar></span><span data-password-bar></span><span data-password-bar></span><small data-password-label></small></div>
  </div>

  <div class="form-field">
    <label class="form-label" for="confirm">Confirm password</label>
    <input id="confirm" name="confirm" class="form-control" type="password" required data-match="password">
  </div>

  <button class="btn btn-primary" type="submit">Save</button>
</form>

Built-in rules

RuleCheck
requiredNot empty
emailEmail format
phoneIranian mobile number (09xxxxxxxxx)
nationalIdIranian national ID with check digit
postal10-digit postal code
ibanIBAN (Sheba) with the IR prefix
urlWeb address
numberNumber (Persian digits supported)
passwordMinimum length (data-min)
patternThe data-pattern regex
min / maxValue length
matchEqual to another field
termsTerms accepted

Persian and Arabic digits are converted to Latin before checking; the user can type «۰۹۱۲۳۴۵۶۷۸۹» or "09123456789".

Programmatic validation

import { validateForm, validateField } from './js/core/form.js';

const { valid, fields } = validateForm(form);
if (!valid) {
  console.log(fields.filter((item) => !item.valid).map((item) => item.field.name));
}

Form widgets

WidgetHTML marker
File drop zone[data-file-drop]
Tag input[data-tag-input]
Password strength meter[data-password-field="#meter"] + [data-password-bar]
Dependent select[data-depends-on="province"]
Stepper[data-stepper]
One-time code[data-otp] with .otp-row and .otp-input
Date pickerinput[type="date"] / [data-datepicker]
Amount input with thousands mask[data-currency-input]

All of these are initialised with initForms() in main.js, and can be applied to freshly rendered content with initForms(scope).

Dynamic forms

To build a form from a definition (like the "Add record" forms):

import { formMarkup, collectValues, openRecordForm } from './js/pages/kit.js';

const markup = formMarkup([
  { name: 'title', label: 'Title', required: true },
  { name: 'price', label: 'Price', type: 'number', rule: 'number' },
  { name: 'status', label: 'Status', type: 'select', options: ['active', 'draft'] },
  { name: 'notes', label: 'Notes', type: 'textarea', col: 2, rows: 4 },
]);
Tip: mark the submit button with the is-loading class so it is disabled and shows a spinner during the request; every form in the template uses this pattern
Warning: never use client-side validation in place of server validation

This layer is for user experience, not security.

النماذج والتحقق

جميع النماذج جاهزة برسائل محلية وتحقق مباشر وعدة حالات مرئية. ولا تُثبَّت أي مكتبة تحقق.

حالات النموذج

الحالةالفئة / الخاصية
افتراضي.form-control
التركيزتلقائي عبر CSS (:focus-visible)
معبّأ.is-filled (بواسطة JS بعد إدخال قيمة)
معطّلdisabled
للقراءة فقطreadonly
خطأ.is-invalid + .field-feedback
ناجح.is-valid
قيد الإرسال.is-loading على زر الإرسال

التحقق التصريحي

<form data-validate novalidate>
  <div class="form-field">
    <label class="form-label" for="email">البريد الإلكتروني</label>
    <input id="email" name="email" class="form-control" type="email" required data-rule="email" data-validate-live="true">
    <p class="field-feedback" hidden></p>
  </div>

  <div class="form-field">
    <label class="form-label" for="nationalId">الرقم الوطني</label>
    <input id="nationalId" name="nationalId" class="form-control" data-rule="nationalId">
  </div>

  <div class="form-field">
    <label class="form-label" for="password">كلمة المرور</label>
    <input id="password" name="password" class="form-control" type="password" required data-rule="password" data-min="8" data-password-field="#password-meter">
    <div class="password-meter" id="password-meter"><span data-password-bar></span><span data-password-bar></span><span data-password-bar></span><span data-password-bar></span><small data-password-label></small></div>
  </div>

  <div class="form-field">
    <label class="form-label" for="confirm">تأكيد كلمة المرور</label>
    <input id="confirm" name="confirm" class="form-control" type="password" required data-match="password">
  </div>

  <button class="btn btn-primary" type="submit">حفظ</button>
</form>

القواعد الجاهزة

القاعدةالتحقق
requiredعدم الفراغ
emailصيغة البريد الإلكتروني
phoneرقم جوال إيراني (09xxxxxxxxx)
nationalIdالرقم الوطني الإيراني مع رقم التحقق
postalرمز بريدي من ١٠ أرقام
ibanرقم IBAN (شبا) بالبادئة IR
urlعنوان ويب
numberعدد (مع دعم الأرقام الفارسية)
passwordالحد الأدنى للطول (data-min)
patternالنمط data-pattern
min / maxطول القيمة
matchالمساواة مع حقل آخر
termsقبول الشروط

تُحوَّل الأرقام الفارسية والعربية إلى لاتينية قبل التحقق؛ فيمكن للمستخدم كتابة «۰۹۱۲۳۴۵۶۷۸۹» أو «٠٩١٢٣٤٥٦٧٨٩» أو «09123456789».

التحقق البرمجي

import { validateForm, validateField } from './js/core/form.js';

const { valid, fields } = validateForm(form);
if (!valid) {
  console.log(fields.filter((item) => !item.valid).map((item) => item.field.name));
}

أدوات النماذج

الأداةالعلامة في HTML
منطقة إفلات الملفات[data-file-drop]
حقل الوسوم[data-tag-input]
مقياس قوة كلمة المرور[data-password-field="#meter"] + [data-password-bar]
قائمة اختيار تابعة[data-depends-on="province"]
عدّاد[data-stepper]
رمز لمرة واحدة[data-otp] مع .otp-row و.otp-input
منتقي التاريخinput[type="date"] / [data-datepicker]
حقل مبلغ بقناع الآلاف[data-currency-input]

تُهيَّأ جميعها بـ initForms() في main.js، ويمكن تطبيقها على المحتوى المعروض حديثاً بـ initForms(scope).

النماذج الديناميكية

لبناء نموذج من تعريف (مثل نماذج «إضافة سجل»):

import { formMarkup, collectValues, openRecordForm } from './js/pages/kit.js';

const markup = formMarkup([
  { name: 'title', label: 'العنوان', required: true },
  { name: 'price', label: 'السعر', type: 'number', rule: 'number' },
  { name: 'status', label: 'الحالة', type: 'select', options: ['active', 'draft'] },
  { name: 'notes', label: 'الوصف', type: 'textarea', col: 2, rows: 4 },
]);
ملاحظة: علّم زر الإرسال بالفئة is-loading ليُعطَّل ويعرض مؤشر تحميل طوال الطلب؛ وهذا النمط مستخدم في جميع نماذج القالب
تحذير: لا تستبدل أبداً التحقق من جهة الخادم بالتحقق من جهة العميل

هذه الطبقة لتجربة المستخدم لا للأمان.