# Компонент FormValidator - Полная документация

## Назначение компонента

`FormValidator` - компонент для валидации форм с поддержкой различных типов полей, кастомных правил валидации, асинхронной проверки и многоязычных сообщений об ошибках. Интегрируется с мультиязычной системой приложения.

## Импорт и регистрация

```coffee
# В основном файле приложения (app/temp.coffee)
components:
    'form-validator': require 'app/shared/FormValidator'
```

## Базовое использование

```pug
//- Минимальное использование
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") Отправить
```

## Полный список props

### `rules` (обязательный)
- **Тип:** `Object`
- **Описание:** Объект с правилами валидации для каждого поля
- **Пример:**
  ```pug
  form-validator(:rules="validationRules")
  ```

### `values` (опциональный)
- **Тип:** `Object`
- **По умолчанию:** `{}`
- **Описание:** Объект с текущими значениями полей
- **Пример:**
  ```pug
  form-validator(:rules="rules" :values="formData")
  ```

### `validateOn` (опциональный)
- **Тип:** `String | Array`
- **По умолчанию:** `'blur'`
- **Допустимые значения:** `'input'`, `'blur'`, `'change'`, `'submit'`
- **Описание:** События для автоматической валидации
- **Пример:**
  ```pug
  form-validator(validateOn="input")
  form-validator(:validateOn="['blur', 'change']")
  ```

### `mode` (опциональный)
- **Тип:** `String`
- **По умолчанию:** `'aggressive'`
- **Допустимые значения:** `'aggressive'`, `'lazy'`, `'passive'`
- **Описание:** Режим валидации
- **Пример:**
  ```pug
  form-validator(mode="lazy")
  ```

### `locale` (опциональный)
- **Тип:** `String`
- **По умолчанию:** `'ru'`
- **Описание:** Локаль для сообщений об ошибках
- **Пример:**
  ```pug
  form-validator(locale="en")
  ```

### `showErrors` (опциональный)
- **Тип:** `Boolean`
- **По умолчанию:** `true`
- **Описание:** Показывать сообщения об ошибках
- **Пример:**
  ```pug
  form-validator(:showErrors="false")
  ```

### `scrollToError` (опциональный)
- **Тип:** `Boolean`
- **По умолчанию:** `true`
- **Описание:** Прокручивать к первой ошибке
- **Пример:**
  ```pug
  form-validator(:scrollToError="false")
  ```

### `class` (опциональный)
- **Тип:** `String | Object | Array`
- **По умолчанию:** `''`
- **Описание:** CSS классы для контейнера
- **Пример:**
  ```pug
  form-validator(class="space-y-4")
  ```

## События (Events)

### `@valid`
- **Описание:** Форма стала валидной
- **Payload:** `{ values: Object, errors: Object }`
- **Пример:**
  ```pug
  form-validator(@valid="onFormValid")
  ```

### `@invalid`
- **Описание:** Форма стала невалидной
- **Payload:** `{ values: Object, errors: Object }`
- **Пример:**
  ```pug
  form-validator(@invalid="onFormInvalid")
  ```

### `@validate`
- **Описание:** Произошла валидация поля
- **Payload:** `{ field: String, isValid: Boolean, error: String }`
- **Пример:**
  ```pug
  form-validator(@validate="onFieldValidate")
  ```

### `@error`
- **Описание:** Появилась ошибка валидации
- **Payload:** `{ field: String, error: String }`
- **Пример:**
  ```pug
  form-validator(@error="onValidationError")
  ```

### `@success`
- **Описание:** Ошибка валидации исправлена
- **Payload:** `{ field: String }`
- **Пример:**
  ```pug
  form-validator(@success="onValidationSuccess")
  ```

## Слоты (Slots)

### `[body]` (основной слот)
- **Описание:** Содержимое формы
- **Scope:** `{ validate: Function, errors: Object, isValid: Boolean, isDirty: Boolean, reset: Function, fields: Object }`
- **Пример:**
  ```pug
  form-validator(:rules="rules" v-slot="{ validate, errors, isValid }")
      form
          input(@blur="validate('email')")
          div(v-if="errors.email") {{ errors.email }}
  ```

### `[error]`
- **Описание:** Кастомное отображение ошибки
- **Scope:** `{ field: String, error: String }`
- **Пример:**
  ```pug
  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]`
- **Описание:** Отображение при успешной валидации
- **Scope:** `{ field: String }`
- **Пример:**
  ```pug
  form-validator(:rules="rules" v-slot="{}")
      form
          input
          template([success]="{ field: 'email' }")
              div(class="success-icon") ✓
  ```

## Правила валидации

### Базовые правила
```coffee
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
```

### Все доступные правила

#### `required`
- **Тип:** `Boolean`
- **Описание:** Поле обязательно для заполнения
- **Пример:**
  ```coffee
  required: true
  ```

#### `email`
- **Тип:** `Boolean`
- **Описание:** Проверка email формата
- **Пример:**
  ```coffee
  email: true
  ```

#### `url`
- **Тип:** `Boolean`
- **Описание:** Проверка URL формата
- **Пример:**
  ```coffee
  url: true
  ```

#### `minLength`
- **Тип:** `Number`
- **Описание:** Минимальная длина значения
- **Пример:**
  ```coffee
  minLength: 3
  ```

#### `maxLength`
- **Тип:** `Number`
- **Описание:** Максимальная длина значения
- **Пример:**
  ```coffee
  maxLength: 255
  ```

#### `min`
- **Тип:** `Number`
- **Описание:** Минимальное числовое значение
- **Пример:**
  ```coffee
  min: 0
  ```

#### `max`
- **Тип:** `Number`
- **Описание:** Максимальное числовое значение
- **Пример:**
  ```coffee
  max: 100
  ```

#### `pattern`
- **Тип:** `RegExp | String`
- **Описание:** Регулярное выражение для проверки
- **Пример:**
  ```coffee
  pattern: /^[a-zA-Z]+$/
  pattern: '^[a-zA-Z]+$'  # Строка также поддерживается
  ```

#### `equals`
- **Тип:** `String | Number | Function`
- **Описание:** Значение должно совпадать с указанным
- **Пример:**
  ```coffee
  equals: 'password'
  equals: (value, values) -> value == values.password
  ```

#### `contains`
- **Тип:** `Array | String`
- **Описание:** Должен содержать указанные символы/типы
- **Пример:**
  ```coffee
  contains: ['uppercase', 'lowercase', 'number', 'special']
  contains: 'abc'
  ```

#### `custom`
- **Тип:** `Function`
- **Описание:** Кастомная функция валидации
- **Пример:**
  ```coffee
  custom: (value, values) ->
      if value.length < 5
          return 'Минимум 5 символов'
      return true
  ```

#### `async`
- **Тип:** `Function`
- **Описание:** Асинхронная функция валидации
- **Пример:**
  ```coffee
  async: (value) ->
      try
          response = await fetch('/api/check-email?email='+value)
          data = await response.json()
          return data.available or 'Email уже используется'
      catch error
          return 'Ошибка проверки'
  ```

## Примеры использования

### 1. Базовая форма регистрации
```pug
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: ->
        # Логика регистрации
```

### 2. Форма с кастомными сообщениями об ошибках
```pug
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}$/, 'Введите корректный номер телефона']
```

### 3. Форма с условной валидацией
```pug
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
```

### 4. Форма с асинхронной валидацией
```pug
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
```

### 5. Форма с групповой валидацией
```pug
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") Проверить контакты
```

## Методы API

### `validate(field?: string)`
- **Описание:** Валидирует поле или всю форму
- **Параметры:** `field` - опциональное имя поля
- **Возвращает:** `Promise<boolean>`
- **Пример:**
  ```coffee
  isValid = await validate('email')
  allValid = await validate()
  ```

### `validateField(field: string)`
- **Описание:** Валидирует конкретное поле
- **Параметры:** `field` - имя поля
- **Возвращает:** `Promise<boolean>`
- **Пример:**
  ```coffee
  isValid = await validateField('email')
  ```

### `validateGroup(group: string)`
- **Описание:** Валидирует группу полей
- **Параметры:** `group` - имя группы
- **Возвращает:** `Promise<boolean>`
- **Пример:**
  ```coffee
  isValid = await validateGroup('contact')
  ```

### `reset()`
- **Описание:** Сбрасывает состояние валидации
- **Пример:**
  ```coffee
  reset()
  ```

### `setErrors(errors: Object)`
- **Описание:** Устанавливает ошибки вручную
- **Параметры:** `errors` - объект с ошибками
- **Пример:**
  ```coffee
  setErrors({ email: 'Email уже существует' })
  ```

### `clearErrors(field?: string)`
- **Описание:** Очищает ошибки поля или все ошибки
- **Параметры:** `field` - опциональное имя поля
- **Пример:**
  ```coffee
  clearErrors('email')
  clearErrors()
  ```

### `getValues()`
- **Описание:** Возвращает текущие значения
- **Возвращает:** `Object`
- **Пример:**
  ```coffee
  values = getValues()
  ```

### `isValid()`
- **Описание:** Проверяет валидность всей формы
- **Возвращает:** `boolean`
- **Пример:**
  ```coffee
  if isValid()
      submitForm()
  ```

## Состояния слота

### `errors`
- **Тип:** `Object`
- **Описание:** Объект с ошибками валидации
- **Пример:**
  ```pug
  span(v-if="errors.email") {{ errors.email }}
  ```

### `isValid`
- **Тип:** `Boolean`
- **Описание:** Вся форма валидна
- **Пример:**
  ```pug
  button(:disabled="!isValid") Отправить
  ```

### `isDirty`
- **Тип:** `Boolean`
- **Описание:** Форма была изменена
- **Пример:**
  ```pug
  button(:disabled="!isDirty") Сохранить изменения
  ```

### `isPending`
- **Тип:** `Object`
- **Описание:** Объект с состояниями асинхронной валидации
- **Пример:**
  ```pug
  div(v-if="isPending.email") Проверка email...
  ```

### `validate`
- **Тип:** `Function`
- **Описание:** Функция для запуска валидации
- **Пример:**
  ```pug
  input(@blur="validate('email')")
  ```

### `reset`
- **Тип:** `Function`
- **Описание:** Функция сброса состояния
- **Пример:**
  ```pug
  button(@click="reset") Сбросить
  ```

## Интеграция с мультиязычной системой

### Мультиязычные сообщения об ошибках
```coffee
# В основном приложении
validationMessages =
    ru:
        required: 'Поле обязательно для заполнения'
        email: 'Введите корректный email адрес'
        minLength: 'Минимум {0} символов'
        # ... другие сообщения
    
    en:
        required: 'Field is required'
        email: 'Enter a valid email address'
        minLength: 'Minimum {0} characters'
```

### Кастомные сообщения в правилах
```coffee
rules:
    email:
        required: [true, 'Пожалуйста, укажите email']
        email: [true, 'Это не похоже на email']
    
    password:
        minLength: [8, 'Пароль должен содержать минимум {0} символов']
```

## Кастомные валидаторы

### Создание кастомного валидатора
```coffee
# Глобальная регистрация
FormValidator.addValidator('phone', (value, params) ->
    phoneRegex = /^\+?[1-9]\d{1,14}$/
    if !phoneRegex.test(value)
        return 'Введите корректный номер телефона'
    return true
)

# Использование
rules:
    phone:
        phone: true
```

### Валидатор с параметрами
```coffee
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
```

## Best Practices

### 1. Используйте соответствующие события валидации
```pug
//- Для мгновенной обратной связи
input(@input="validateOn === 'input' && validate('email')")

//- Для окончательной проверки
input(@blur="validate('email')")
```

### 2. Группируйте связанные поля
```coffee
rules:
    contact:
        group: ['email', 'phone']
        validator: (values) ->
            if !values.email && !values.phone
                return 'Укажите email или телефон'
            return true
```

### 3. Используйте асинхронную валидацию для проверок на сервере
```coffee
async: async (value) ->
    try
        available = await checkAvailability(value)
        return available || 'Уже занято'
    catch error
        return 'Ошибка проверки'
```

### 4. Предоставляйте понятные сообщения об ошибках
```coffee
password:
    minLength: [8, 'Пароль должен содержать не менее 8 символов']
    contains: [
        ['uppercase', 'Хотя бы одна заглавная буква'],
        ['number', 'Хотя бы одна цифра']
    ]
```

### 5. Тестируйте валидацию
```coffee
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 предоставляет мощную и гибкую систему валидации форм с поддержкой различных типов правил, асинхронной проверки, мультиязычности и кастомизации.
