|
|
3 周之前 | |
|---|---|---|
| .. | ||
| README.md | 3 周之前 | |
| index.coffee | 3 周之前 | |
| index.pug | 3 周之前 | |
| index.styl | 3 周之前 | |
FormValidator - компонент для валидации форм с поддержкой различных типов полей, кастомных правил валидации, асинхронной проверки и многоязычных сообщений об ошибках. Интегрируется с мультиязычной системой приложения.
https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/src/master/vue/app/shared/FormValidator/
# В основном файле приложения (app/temp.coffee)
components:
'form-validator': require 'app/shared/FormValidator'
//- Минимальное использование
form-validator(:rules="rules" v-slot="{ validate, errors }")
input(v-model="email" @blur="validate('email')")
div(v-if="errors.email") {{ errors.email }}
//- С автоматической валидацией
form-validator(:rules="rules" :values="formData" v-slot="{ validate, errors, isValid }")
form(@submit.prevent="isValid && submitForm()")
input(v-model="formData.email" @blur="validate('email')")
span(v-if="errors.email" class="error") {{ errors.email }}
button(:disabled="!isValid") Отправить
rules (обязательный)ObjectПример:
form-validator(:rules="validationRules")
values (опциональный)Object{}Пример:
form-validator(:rules="rules" :values="formData")
validateOn (опциональный)String | Array'blur''input', 'blur', 'change', 'submit'Пример:
form-validator(validateOn="input")
form-validator(:validateOn="['blur', 'change']")
mode (опциональный)String'aggressive''aggressive', 'lazy', 'passive'Пример:
form-validator(mode="lazy")
locale (опциональный)String'ru'Пример:
form-validator(locale="en")
showErrors (опциональный)BooleantrueПример:
form-validator(:showErrors="false")
scrollToError (опциональный)BooleantrueПример:
form-validator(:scrollToError="false")
class (опциональный)String | Object | Array''Пример:
form-validator(class="space-y-4")
@valid{ values: Object, errors: Object }Пример:
form-validator(@valid="onFormValid")
@invalid{ values: Object, errors: Object }Пример:
form-validator(@invalid="onFormInvalid")
@validate{ field: String, isValid: Boolean, error: String }Пример:
form-validator(@validate="onFieldValidate")
@error{ field: String, error: String }Пример:
form-validator(@error="onValidationError")
@success{ field: String }Пример:
form-validator(@success="onValidationSuccess")
[body] (основной слот){ validate: Function, errors: Object, isValid: Boolean, isDirty: Boolean, reset: Function, fields: Object }Пример:
form-validator(:rules="rules" v-slot="{ validate, errors, isValid }")
form
input(@blur="validate('email')")
div(v-if="errors.email") {{ errors.email }}
[error]{ field: String, error: String }Пример:
form-validator(:rules="rules" v-slot="{ errors }")
form
input
template([error]="{ field: 'email', error: errors.email }")
div(v-if="error" class="custom-error") {{ error }}
[success]{ field: String }Пример:
form-validator(:rules="rules" v-slot="{}")
form
input
template([success]="{ field: 'email' }")
div(class="success-icon") ✓
validationRules =
email:
required: true
email: true
maxLength: 255
password:
required: true
minLength: 8
contains: ['uppercase', 'lowercase', 'number']
phone:
required: true
pattern: /^\+?[1-9]\d{1,14}$/
name:
required: true
minLength: 2
maxLength: 50
requiredBooleanПример:
required: true
emailBooleanПример:
email: true
urlBooleanПример:
url: true
minLengthNumberПример:
minLength: 3
maxLengthNumberПример:
maxLength: 255
minNumberПример:
min: 0
maxNumberПример:
max: 100
patternRegExp | StringПример:
pattern: /^[a-zA-Z]+$/
pattern: '^[a-zA-Z]+$' # Строка также поддерживается
equalsString | Number | FunctionПример:
equals: 'password'
equals: (value, values) -> value == values.password
containsArray | StringПример:
contains: ['uppercase', 'lowercase', 'number', 'special']
contains: 'abc'
customFunctionПример:
custom: (value, values) ->
if value.length < 5
return 'Минимум 5 символов'
return true
asyncFunctionПример:
async: (value) ->
try
response = await fetch('/api/check-email?email='+value)
data = await response.json()
return data.available or 'Email уже используется'
catch error
return 'Ошибка проверки'
form-validator(
:rules="registerRules"
:values="formData"
v-slot="{ validate, errors, isValid, isDirty }"
)
form(@submit.prevent="isValid && register()" class="space-y-4")
div
label(for="email" class="block text-sm font-medium") Email
input(
id="email"
v-model="formData.email"
@blur="validate('email')"
@input="validateOn === 'input' && validate('email')"
type="email"
class="w-full p-2 border rounded"
:class="{ 'border-red-500': errors.email }"
)
div(v-if="errors.email" class="text-red-500 text-sm mt-1") {{ errors.email }}
div
label(for="password" class="block text-sm font-medium") Пароль
input(
id="password"
v-model="formData.password"
@blur="validate('password')"
type="password"
class="w-full p-2 border rounded"
:class="{ 'border-red-500': errors.password }"
)
div(v-if="errors.password" class="text-red-500 text-sm mt-1") {{ errors.password }}
div
label(for="confirmPassword" class="block text-sm font-medium") Подтверждение пароля
input(
id="confirmPassword"
v-model="formData.confirmPassword"
@blur="validate('confirmPassword')"
type="password"
class="w-full p-2 border rounded"
:class="{ 'border-red-500': errors.confirmPassword }"
)
div(v-if="errors.confirmPassword" class="text-red-500 text-sm mt-1") {{ errors.confirmPassword }}
button(
type="submit"
class="w-full bg-blue-500 text-white p-2 rounded disabled:bg-gray-400"
:disabled="!isValid || !isDirty"
) Зарегистрироваться
//- В компоненте
data: ->
formData:
email: ''
password: ''
confirmPassword: ''
registerRules:
email:
required: true
email: true
maxLength: 255
async: @checkEmailAvailability
password:
required: true
minLength: 8
contains: ['uppercase', 'lowercase', 'number']
confirmPassword:
required: true
equals: (value, values) -> value == values.password
methods:
checkEmailAvailability: async (email) ->
if !email or email.length < 3
return true
try
response = await fetch('/api/check-email?email='+encodeURIComponent(email))
data = await response.json()
return data.available or 'Этот email уже используется'
catch error
return 'Ошибка проверки email'
register: ->
# Логика регистрации
form-validator(
:rules="rules"
:values="formData"
v-slot="{ validate, errors }"
)
form(class="space-y-4")
div
label Имя
input(
v-model="formData.name"
@blur="validate('name')"
:class="{ 'error-border': errors.name }"
)
template([error]="{ field: 'name', error: errors.name }")
div(v-if="error" class="custom-error-message")
icon(name="warning")
span {{ error }}
div
label Телефон
input(
v-model="formData.phone"
@blur="validate('phone')"
:class="{ 'error-border': errors.phone }"
)
template([success]="{ field: 'phone' }")
div(class="success-message")
icon(name="check")
span Телефон корректен
//- Правила с кастомными сообщениями
rules:
name:
required: [true, 'Пожалуйста, укажите ваше имя']
minLength: [2, 'Имя должно содержать минимум 2 символа']
maxLength: [50, 'Имя не должно превышать 50 символов']
phone:
required: [true, 'Телефон обязателен для заполнения']
pattern: [/^\+?[1-9]\d{1,14}$/, 'Введите корректный номер телефона']
form-validator(
:rules="conditionalRules"
:values="formData"
v-slot="{ validate, errors, isValid }"
)
form
div
label Тип пользователя
select(v-model="formData.userType" @change="validate()")
option(value="individual") Физическое лицо
option(value="company") Юридическое лицо
div(v-if="formData.userType === 'company'")
label Название компании
input(
v-model="formData.companyName"
@blur="validate('companyName')"
)
div(v-if="errors.companyName") {{ errors.companyName }}
div(v-if="formData.userType === 'company'")
label ИНН
input(
v-model="formData.inn"
@blur="validate('inn')"
)
div(v-if="errors.inn") {{ errors.inn }}
//- Условные правила
conditionalRules:
userType:
required: true
companyName:
required: (value, values) -> values.userType == 'company'
minLength: (value, values) ->
if values.userType == 'company' then 2 else true
inn:
required: (value, values) -> values.userType == 'company'
pattern: (value, values) ->
if values.userType == 'company'
return /^\d{10,12}$/
return true
form-validator(
:rules="asyncRules"
:values="formData"
v-slot="{ validate, errors, isValid, isPending }"
)
form
div
label Имя пользователя
input(
v-model="formData.username"
@blur="validate('username')"
:class="{ 'validating': isPending.username }"
)
div(v-if="errors.username") {{ errors.username }}
div(v-if="isPending.username" class="loading") Проверка...
button(:disabled="!isValid || isPending.username") Сохранить
//- Асинхронные правила
asyncRules:
username:
required: true
minLength: 3
maxLength: 20
pattern: /^[a-zA-Z0-9_]+$/
async: @checkUsername
form-validator(
:rules="groupRules"
:values="formData"
v-slot="{ validate, errors, validateField, validateGroup }"
)
form
fieldset
legend Контактная информация
div
label Email
input(v-model="formData.email" @blur="validateField('email')")
div(v-if="errors.email") {{ errors.email }}
div
label Телефон
input(v-model="formData.phone" @blur="validateField('phone')")
div(v-if="errors.phone") {{ errors.phone }}
button(@click="validateGroup('contact')" type="button") Проверить контакты
validate(field?: string)field - опциональное имя поляPromise<boolean>Пример:
isValid = await validate('email')
allValid = await validate()
validateField(field: string)field - имя поляPromise<boolean>Пример:
isValid = await validateField('email')
validateGroup(group: string)group - имя группыPromise<boolean>Пример:
isValid = await validateGroup('contact')
reset()Пример:
reset()
setErrors(errors: Object)errors - объект с ошибкамиПример:
setErrors({ email: 'Email уже существует' })
clearErrors(field?: string)field - опциональное имя поляПример:
clearErrors('email')
clearErrors()
getValues()ObjectПример:
values = getValues()
isValid()booleanПример:
if isValid()
submitForm()
errorsObjectПример:
span(v-if="errors.email") {{ errors.email }}
isValidBooleanПример:
button(:disabled="!isValid") Отправить
isDirtyBooleanПример:
button(:disabled="!isDirty") Сохранить изменения
isPendingObjectПример:
div(v-if="isPending.email") Проверка email...
validateFunctionПример:
input(@blur="validate('email')")
resetFunctionПример:
button(@click="reset") Сбросить
# В основном приложении
validationMessages =
ru:
required: 'Поле обязательно для заполнения'
email: 'Введите корректный email адрес'
minLength: 'Минимум {0} символов'
# ... другие сообщения
en:
required: 'Field is required'
email: 'Enter a valid email address'
minLength: 'Minimum {0} characters'
rules:
email:
required: [true, 'Пожалуйста, укажите email']
email: [true, 'Это не похоже на email']
password:
minLength: [8, 'Пароль должен содержать минимум {0} символов']
# Глобальная регистрация
FormValidator.addValidator('phone', (value, params) ->
phoneRegex = /^\+?[1-9]\d{1,14}$/
if !phoneRegex.test(value)
return 'Введите корректный номер телефона'
return true
)
# Использование
rules:
phone:
phone: true
FormValidator.addValidator('fileSize', (value, params) ->
if value && value.size > params.maxSize
return "Файл слишком большой. Максимум: #{params.maxSize} bytes"
return true
)
# Использование
rules:
avatar:
fileSize: { maxSize: 5 * 1024 * 1024 } # 5MB
//- Для мгновенной обратной связи
input(@input="validateOn === 'input' && validate('email')")
//- Для окончательной проверки
input(@blur="validate('email')")
rules:
contact:
group: ['email', 'phone']
validator: (values) ->
if !values.email && !values.phone
return 'Укажите email или телефон'
return true
async: async (value) ->
try
available = await checkAvailability(value)
return available || 'Уже занято'
catch error
return 'Ошибка проверки'
password:
minLength: [8, 'Пароль должен содержать не менее 8 символов']
contains: [
['uppercase', 'Хотя бы одна заглавная буква'],
['number', 'Хотя бы одна цифра']
]
describe('FormValidator', ->
it('should validate email correctly', ->
validator = new FormValidator(rules: email: email: true)
result = await validator.validateField('email', 'invalid-email')
expect(result).toBe(false)
expect(validator.errors.email).toBe('Введите корректный email')
result = await validator.validateField('email', 'test@example.com')
expect(result).toBe(true)
expect(validator.errors.email).toBeNull()
)
)
Компонент FormValidator предоставляет мощную и гибкую систему валидации форм с поддержкой различных типов правил, асинхронной проверки, мультиязычности и кастомизации.