Nette Documentation Preview

syntax
Валідація форм
**************


Обов'язкові для заповнення елементи .[#toc-required-controls]
=============================================================

Елементи керування позначаються як обов'язкові за допомогою методу `setRequired()`, аргументом якого є текст [повідомлення про помилку |#Сообщения об ошибке], що відображається, якщо користувач не заповнить його. Якщо аргумент не вказано, використовується повідомлення про помилку за замовчуванням.

```php
$form->addText('name', 'Имя:')
	->setRequired('Пожалуйста, заполните ваше имя.');
```


Правила .[#toc-rules]
=====================

Ми додаємо правила перевірки до елементів управління за допомогою методу `addRule()`. Перший параметр - це правило, другий - [повідомлення про помилку |#Сообщения об ошибке], а третій - аргумент правила перевірки.

```php
$form->addPassword('password', 'Пароль:')
	->addRule($form::MinLength, 'Пароль должен состоять не менее чем из %d символов', 8);
```

**Правила перевірки перевіряються тільки в тому випадку, якщо користувач заповнив елемент.**

Nette постачається з низкою попередньо визначених правил, імена яких є константами класу `Nette\Forms\Form`. Ми можемо застосувати ці правила до всіх елементів:

| константа | опис | аргументи
|-------
| `Required` | псевдонім `setRequired()` | -
| `Filled` | псевдонім `setRequired()` | -
| `Blank` | не повинно бути заповнено | -
| `Equal` | значення дорівнює параметру | `mixed`
| `NotEqual` | значення не дорівнює параметру | `mixed`
| `IsIn` | значення дорівнює деякому елементу масиву | `array`
| `IsNotIn` | значення не дорівнює жодному елементу масиву | `array`
| `Valid` | введення проходить валідацію (для [Умови |#Условия])| -


Текстові дані
-------------

Для елементів `addText()`, `addPassword()`, `addTextArea()`, `addEmail()`, `addInteger()`, `addFloat()` також можна застосувати деякі з наступних правил:

| `MinLength` | мінімальна довжина рядка | `int`
| `MaxLength` | максимальна довжина рядка | `int`
| `Length` | довжина в діапазоні або точна довжина | пара `[int, int]` або `int`
| `Email` | дійсна адреса електронної пошти | -
| `URL` | дійсний URL | -
| `Pattern` | відповідає регулярному шаблону | `string`
| `PatternInsensitive` | як `Pattern`, але без урахування регістру. | `string`
| `Integer` | ціле число | -
| `Numeric` | псевдонім `Integer` | - -
| `Float` | ціле число або число з плаваючою крапкою | -
| `Min` | мінімум цілочисельного значення | `int\|float`
| `Max` | максимум цілочисельного значення | `int\|float`
| `Range` | значення в діапазоні | пара `[int\|float, int\|float]`

Правила `Integer`, `Numeric` і `Float` автоматично перетворюють значення в ціле (або плаваюче відповідно). Більше того, правило `URL` також приймає адресу без схеми (наприклад, `nette.org`) і доповнює схему (`https://nette.org`).
Вирази в `Pattern` і `PatternInsensitive` мають бути дійсними для всього значення, тобто так, ніби воно було обгорнуте в символи `^` и `$`.


Кількість позицій .[#toc-number-of-items]
-----------------------------------------

Для елементів `addMultiUpload()`, `addCheckboxList()`, `addMultiSelect()` ви також можете використовувати наступні правила для обмеження кількості обраних елементів або завантажених файлів:

| `MinLength` | мінімальна кількість `int`
| `MaxLength` | максимальна кількість `int`
| `Length` | кількість в діапазоні або точна кількість | пар `[int, int]` або `int`


Завантаження файлу
------------------

Для елементів керування `addUpload()`, `addMultiUpload()` також можуть бути використані такі правила:

| `MaxFileSize` | максимальний розмір файлу в байтах | `int`
| `MimeType` | Тип MIME, приймає підстановні знаки (`'video/*'`) | `string\|string[]`
| `Image` | завантажений файл є JPEG, PNG, GIF, WebP | -
| `Pattern` | ім'я файлу відповідає регулярному виразу | `string`
| `PatternInsensitive` | як `Pattern`, але без урахування регістру. | `string`

Для `MimeType` і `Image` потрібне розширення PHP `fileinfo`. Належність файлу або зображення до потрібного типу визначається за його сигнатурою. Цілісність усього файлу не перевіряється. Ви можете дізнатися, чи не пошкоджено зображення, наприклад, спробувавши [завантажити його |http:request#toImage].


Повідомлення про помилки .[#toc-error-messages]
===============================================

Усі зумовлені правила, крім `Pattern` і `PatternInsensitive`, мають повідомлення про помилку за замовчуванням, тому їх можна опустити. Однак, передавши і сформулювавши всі індивідуальні повідомлення, ви зробите форму зручнішою для користувача.

Ви можете змінити повідомлення за замовчуванням у [configuration |forms:configuration], змінюючи тексти в масиві `Nette\Forms\Validator::$messages` або використовуючи [translator |rendering#translating].

У тексті повідомлень про помилки можна використовувати такі символи підстановки:

| `%d` | поступово замінює правила після аргументів
| `%n$d` | замінюється на n-й аргумент правила
| `%label` | замінює на мітку поля (без двокрапки)
| `%name` | замінює ім'я поля (наприклад, `name`)
| `%value` | замінюється значенням, введеним користувачем

```php
$form->addText('name', 'Имя:')
	->setRequired('Пожалуйста, заполните %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'не менее %d и не более %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'не более %2$d и не менее %1$d', [5, 10]);
```


Умови .[#toc-conditions]
========================

Крім правил валідації, можна задати умови. Вони встановлюються так само, як і правила, але ми використовуємо `addRule()` замість `addCondition()` і, звісно, залишаємо їх без повідомлення про помилку (умова просто запитує):

```php
$form->addPassword('password', 'Пароль:')
	// якщо пароль не довший за 8 символів ...
	->addCondition($form::MaxLength, 8)
		// ... тоді він має містити число
		->addRule($form::Pattern, 'Повинен містити номер', '.*[0-9].*');
```

Умова може бути прив'язана до елемента, відмінного від поточного, за допомогою `addConditionOn()`. Перший параметр - це посилання на поле. У наступному випадку електронна пошта знадобиться тільки в тому випадку, якщо прапорець установлено (тобто його значення дорівнює `true`):

```php
$form->addCheckbox('newsletters', 'надсилати мені інформаційні бюлетені');

$form->addEmail('email', 'Імейл:')
	// якщо прапорець встановлено ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... вимагати електронну пошту
		->setRequired('Введіть свою адресу електронної пошти');
```

Умови можуть бути згруповані в складні структури за допомогою методів `elseCondition()` і `endCondition()`.

```php
$form->addText(/* ... */)
	->addCondition(/* ... */) // якщо виконується перша умова
		->addConditionOn(/* ... */) // і друга умова на іншому елементі теж
			->addRule(/* ... */) // вимагають дотримання цього правила
		->elseCondition() // якщо друга умова не виконується
			->addRule(/* ... */) // вимагають дотримання цих правил
			->addRule(/* ... */)
		->endCondition() // ми повертаємося до першої умови
		->addRule(/* ... */);
```

У Nette дуже легко реагувати на виконання або невиконання умови на стороні JavaScript, використовуючи метод `toggle()`, див. [Динамічний JavaScript |#Динамический JavaScript].


Посилання між елементами керування .[#toc-references-between-controls]
======================================================================

Аргумент правила або умови може бути посиланням на інший елемент. Наприклад, ви можете динамічно підтвердити, що `text` має стільки символів, скільки вказано в полі `length`:

```php
$form->addInteger('length');
$form->addText('text')
	->addRule($form::Length, null, $form['length']);
```


Користувацькі правила та умови .[#toc-custom-rules-and-conditions]
==================================================================

Іноді ми стикаємося з ситуацією, коли вбудованих правил валідації в Nette недостатньо, і нам потрібно перевірити дані від користувача по-своєму. У Nette це дуже просто!

Ви можете передати будь-який зворотний виклик як перший параметр у методи `addRule()` або `addCondition()`. Зворотний виклик приймає сам елемент як перший параметр і повертає булеве значення, що вказує на успішність перевірки. Під час додавання правила за допомогою `addRule()` можна передати додаткові аргументи, які передаються як другий параметр.

Таким чином, користувацький набір валідаторів може бути створений як клас зі статичними методами:

```php
class MyValidators
{
	// перевіряє, чи ділиться значення на аргумент
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// додаткові валідатори
	}
}
```

Далі використання дуже просте:

```php
$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'Значение должно быть кратно %d',
		8,
	);
```

Користувацькі правила валідації також можуть бути додані в JavaScript. Єдиною вимогою є те, що правило має бути статичним методом. Його ім'я для валідатора JavaScript створюється шляхом з'єднання імені класу без зворотних косих рисок `\`, подчеркивания `_` та імені методу. Наприклад, запишіть `App\MyValidators::validateDivisibility` як `AppMyValidators_validateDivisibility` і додайте його в об'єкт `Nette.validators`:

```js
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
	return val % args === 0;
};
```


Подія onValidate .[#toc-event-onvalidate]
=========================================

Після відправлення форми перевірка виконується шляхом перевірки окремих правил, доданих за допомогою `addRule()`, і подальшого виклику [події |nette:glossary#Events] `onValidate`. Її обробник може бути використаний для додаткової перевірки, зазвичай для перевірки правильності комбінації значень у кількох елементах форми.

Якщо виявлено помилку, вона передається у форму за допомогою методу `addError()`. Це може бути викликано як на певному елементі, так і безпосередньо на формі.

```php
protected function createComponentSignInForm(): Form
{
	$form = new Form;
	// ...
	$form->onValidate[] = [$this, 'validateSignInForm'];
	return $form;
}

public function validateSignInForm(Form $form, \stdClass $data): void
{
	if ($data->foo > 1 && $data->bar > 5) {
		$form->addError('Ця комбінація неможлива.');
	}
}
```


Помилки обробки .[#toc-processing-errors]
=========================================

У багатьох випадках ми виявляємо помилку, коли обробляємо дійсну форму, наприклад, коли ми записуємо новий запис у базу даних і стикаємося з дублюючим ключем. У цьому випадку ми передаємо помилку назад у форму за допомогою методу `addError()`. Це може бути викликано як на певному елементі, так і безпосередньо на формі:

```php
try {
	$data = $form->getValues();
	$this->user->login($data->username, $data->password);
	$this->redirect('Home:');

} catch (Nette\Security\AuthenticationException $e) {
	if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) {
		$form->addError('Неверный пароль.');
	}
}
```

Якщо можливо, ми рекомендуємо додати помилку безпосередньо до елемента форми, оскільки вона відображатиметься поруч із ним під час використання рендерингу за замовчуванням.

```php
$form['date']->addError('Извините, эта дата уже занята.');
```

Ви можете викликати `addError()` кілька разів, щоб передати кілька повідомлень про помилки формі або елементу. Ви отримуєте їх за допомогою функції `getErrors()`.

Зверніть увагу, що `$form->getErrors()` повертає зведення всіх повідомлень про помилки, навіть тих, що були передані безпосередньо окремим елементам, а не тільки безпосередньо формі. Повідомлення про помилки, передані тільки формі, витягуються через `$form->getOwnErrors()`.


Зміна вхідних значень .[#toc-modifying-input-values]
====================================================

Використовуючи метод `addFilter()`, ми можемо змінити значення, введене користувачем. У цьому прикладі ми будемо допускати і видаляти пробіли в поштовому індексі:

```php
$form->addText('zip', 'Поштовий індекс:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // видалити пробіли з поштового індексу
	})
	->addRule($form::Pattern, 'Поштовий індекс не складається з п'яти цифр', '\d{5}');
```

Фільтр увімкнено між правилами перевірки й умовами і тому він залежить від порядку проходження методів, тобто фільтр і правило викликаються в тому самому порядку, що й порядок проходження методів `addFilter()` і `addRule()`.


Валідація JavaScript .[#toc-javascript-validation]
==================================================

Мова правил та умов перевірки є потужною. Незважаючи на те, що всі конструкції працюють як на стороні сервера, так і на стороні клієнта, у JavaScript. Правила передаються в HTML-атрибутах `data-nette-rules` у вигляді JSON.
Сама валідація обробляється іншим скриптом, який перехоплює всі події форми `submit`, перебирає всі дані, що вводяться, і запускає відповідні валідації.

Цей скрипт - `netteForms.js`, який доступний з декількох можливих джерел:

Ви можете вбудувати сценарій безпосередньо в HTML-сторінку з CDN:

```latte
<script src="https://unpkg.com/nette-forms@3/src/assets/netteForms.js"></script>
```

Або скопіюйте локально в загальну папку проєкту (наприклад, із сайту `vendor/nette/forms/src/assets/netteForms.min.js`):

```latte
<script src="/path/to/netteForms.min.js"></script>
```

Або встановіть через [npm |https://www.npmjs.com/package/nette-forms]:

```shell
npm install nette-forms
```

А потім завантажте та запустіть:

```js
import netteForms from 'nette-forms';
netteForms.initOnLoad();
```

Крім того, ви можете завантажити його безпосередньо з папки `vendor`:

```js
import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();
```


Динамічний JavaScript .[#toc-dynamic-javascript]
================================================

Ви хочете відображати поля адреси тільки в тому випадку, якщо користувач вирішить відправити товар поштою? Немає проблем. Ключем є пара методів `addCondition()` і `toggle()`:

```php
$form->addCheckbox('send_it')
	->addCondition($form::Equal, true)
		->toggle('#address-container');
```

Цей код говорить, що при виконанні умови, тобто при встановленні прапорця, HTML-елемент `#address-container` стане видимим. І навпаки. Отже, ми поміщаємо елементи форми з адресою одержувача в контейнер із цим ID, і під час натискання на прапорець вони приховуються або показуються. Цим займається скрипт `netteForms.js`.

Будь-який селектор може бути переданий як аргумент методу `toggle()`. З історичних причин буквено-цифровий рядок без інших спеціальних символів розглядають як ідентифікатор елемента, так само як якби йому передував символ `#`. Второй необязательный параметр позволяет нам изменить поведение, т. е. если бы мы использовали `toggle('#address-container', false)`, елемент відображали б тільки в разі знятого прапорця.

Реалізація JavaScript за замовчуванням змінює властивість `hidden` для елементів. Однак ми можемо легко змінити поведінку, наприклад, додавши анімацію. Просто перевизначте метод `Nette.toggle` у JavaScript за допомогою власного рішення:

```js
Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// скрыть или показать 'el' в зависимости от значения 'visible'
	});
};
```


Вимкнення валідації .[#toc-disabling-validation]
================================================

У деяких випадках необхідно відключити валідацію. Якщо кнопка submit не повинна виконувати перевірку після відправлення (наприклад, кнопка *Скасування* або *Предварительный просмотр*), ви можете відключити перевірку, викликавши `$submit->setValidationScope([])`. Ви також можете перевірити форму частково, вказавши елементи для перевірки.

```php
$form->addText('name')
	->setRequired();

$details = $form->addContainer('details');
$details->addInteger('age')
	->setRequired('age');
$details->addInteger('age2')
	->setRequired('age2');

$form->addSubmit('send1'); // Перевіряє всю форму
$form->addSubmit('send2')
	->setValidationScope([]); // Нічого не підтверджує
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Перевіряє тільки поле 'ім'я'
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Перевіряється тільки поле 'вік'
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Перевіряє контейнер 'details'
```

[Подія onValidate |#Событие onValidate] на формі викликається завжди і не залежить від `setValidationScope`. Подія `onValidate` на контейнері викликається тільки тоді, коли цей контейнер вказано для часткової валідації.

Валідація форм

Обов'язкові для заповнення елементи

Елементи керування позначаються як обов'язкові за допомогою методу setRequired(), аргументом якого є текст повідомлення про помилку, що відображається, якщо користувач не заповнить його. Якщо аргумент не вказано, використовується повідомлення про помилку за замовчуванням.

$form->addText('name', 'Имя:')
	->setRequired('Пожалуйста, заполните ваше имя.');

Правила

Ми додаємо правила перевірки до елементів управління за допомогою методу addRule(). Перший параметр – це правило, другий – повідомлення про помилку, а третій – аргумент правила перевірки.

$form->addPassword('password', 'Пароль:')
	->addRule($form::MinLength, 'Пароль должен состоять не менее чем из %d символов', 8);

Правила перевірки перевіряються тільки в тому випадку, якщо користувач заповнив елемент.

Nette постачається з низкою попередньо визначених правил, імена яких є константами класу Nette\Forms\Form. Ми можемо застосувати ці правила до всіх елементів:

константа опис аргументи
Required псевдонім setRequired()
Filled псевдонім setRequired()
Blank не повинно бути заповнено
Equal значення дорівнює параметру mixed
NotEqual значення не дорівнює параметру mixed
IsIn значення дорівнює деякому елементу масиву array
IsNotIn значення не дорівнює жодному елементу масиву array
Valid введення проходить валідацію (для Умови)

Текстові дані

Для елементів addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() також можна застосувати деякі з наступних правил:

MinLength мінімальна довжина рядка int
MaxLength максимальна довжина рядка int
Length довжина в діапазоні або точна довжина пара [int, int] або int
Email дійсна адреса електронної пошти
URL дійсний URL
Pattern відповідає регулярному шаблону string
PatternInsensitive як Pattern, але без урахування регістру. string
Integer ціле число
Numeric псевдонім Integer – –
Float ціле число або число з плаваючою крапкою
Min мінімум цілочисельного значення int|float
Max максимум цілочисельного значення int|float
Range значення в діапазоні пара [int|float, int|float]

Правила Integer, Numeric і Float автоматично перетворюють значення в ціле (або плаваюче відповідно). Більше того, правило URL також приймає адресу без схеми (наприклад, nette.org) і доповнює схему (https://nette.org). Вирази в Pattern і PatternInsensitive мають бути дійсними для всього значення, тобто так, ніби воно було обгорнуте в символи ^ и $.

Кількість позицій

Для елементів addMultiUpload(), addCheckboxList(), addMultiSelect() ви також можете використовувати наступні правила для обмеження кількості обраних елементів або завантажених файлів:

MinLength мінімальна кількість int
MaxLength максимальна кількість int
Length кількість в діапазоні або точна кількість пар [int, int] або int

Завантаження файлу

Для елементів керування addUpload(), addMultiUpload() також можуть бути використані такі правила:

MaxFileSize максимальний розмір файлу в байтах int
MimeType Тип MIME, приймає підстановні знаки ('video/*') string|string[]
Image завантажений файл є JPEG, PNG, GIF, WebP
Pattern ім'я файлу відповідає регулярному виразу string
PatternInsensitive як Pattern, але без урахування регістру. string

Для MimeType і Image потрібне розширення PHP fileinfo. Належність файлу або зображення до потрібного типу визначається за його сигнатурою. Цілісність усього файлу не перевіряється. Ви можете дізнатися, чи не пошкоджено зображення, наприклад, спробувавши завантажити його.

Повідомлення про помилки

Усі зумовлені правила, крім Pattern і PatternInsensitive, мають повідомлення про помилку за замовчуванням, тому їх можна опустити. Однак, передавши і сформулювавши всі індивідуальні повідомлення, ви зробите форму зручнішою для користувача.

Ви можете змінити повідомлення за замовчуванням у configuration, змінюючи тексти в масиві Nette\Forms\Validator::$messages або використовуючи translator.

У тексті повідомлень про помилки можна використовувати такі символи підстановки:

%d поступово замінює правила після аргументів
%n$d замінюється на n-й аргумент правила
%label замінює на мітку поля (без двокрапки)
%name замінює ім'я поля (наприклад, name)
%value замінюється значенням, введеним користувачем
$form->addText('name', 'Имя:')
	->setRequired('Пожалуйста, заполните %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'не менее %d и не более %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'не более %2$d и не менее %1$d', [5, 10]);

Умови

Крім правил валідації, можна задати умови. Вони встановлюються так само, як і правила, але ми використовуємо addRule() замість addCondition() і, звісно, залишаємо їх без повідомлення про помилку (умова просто запитує):

$form->addPassword('password', 'Пароль:')
	// якщо пароль не довший за 8 символів ...
	->addCondition($form::MaxLength, 8)
		// ... тоді він має містити число
		->addRule($form::Pattern, 'Повинен містити номер', '.*[0-9].*');

Умова може бути прив'язана до елемента, відмінного від поточного, за допомогою addConditionOn(). Перший параметр – це посилання на поле. У наступному випадку електронна пошта знадобиться тільки в тому випадку, якщо прапорець установлено (тобто його значення дорівнює true):

$form->addCheckbox('newsletters', 'надсилати мені інформаційні бюлетені');

$form->addEmail('email', 'Імейл:')
	// якщо прапорець встановлено ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... вимагати електронну пошту
		->setRequired('Введіть свою адресу електронної пошти');

Умови можуть бути згруповані в складні структури за допомогою методів elseCondition() і endCondition().

$form->addText(/* ... */)
	->addCondition(/* ... */) // якщо виконується перша умова
		->addConditionOn(/* ... */) // і друга умова на іншому елементі теж
			->addRule(/* ... */) // вимагають дотримання цього правила
		->elseCondition() // якщо друга умова не виконується
			->addRule(/* ... */) // вимагають дотримання цих правил
			->addRule(/* ... */)
		->endCondition() // ми повертаємося до першої умови
		->addRule(/* ... */);

У Nette дуже легко реагувати на виконання або невиконання умови на стороні JavaScript, використовуючи метод toggle(), див. Динамічний JavaScript.

Посилання між елементами керування

Аргумент правила або умови може бути посиланням на інший елемент. Наприклад, ви можете динамічно підтвердити, що text має стільки символів, скільки вказано в полі length:

$form->addInteger('length');
$form->addText('text')
	->addRule($form::Length, null, $form['length']);

Користувацькі правила та умови

Іноді ми стикаємося з ситуацією, коли вбудованих правил валідації в Nette недостатньо, і нам потрібно перевірити дані від користувача по-своєму. У Nette це дуже просто!

Ви можете передати будь-який зворотний виклик як перший параметр у методи addRule() або addCondition(). Зворотний виклик приймає сам елемент як перший параметр і повертає булеве значення, що вказує на успішність перевірки. Під час додавання правила за допомогою addRule() можна передати додаткові аргументи, які передаються як другий параметр.

Таким чином, користувацький набір валідаторів може бути створений як клас зі статичними методами:

class MyValidators
{
	// перевіряє, чи ділиться значення на аргумент
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// додаткові валідатори
	}
}

Далі використання дуже просте:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'Значение должно быть кратно %d',
		8,
	);

Користувацькі правила валідації також можуть бути додані в JavaScript. Єдиною вимогою є те, що правило має бути статичним методом. Його ім'я для валідатора JavaScript створюється шляхом з'єднання імені класу без зворотних косих рисок \, подчеркивания _ та імені методу. Наприклад, запишіть App\MyValidators::validateDivisibility як AppMyValidators_validateDivisibility і додайте його в об'єкт Nette.validators:

Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
	return val % args === 0;
};

Подія onValidate

Після відправлення форми перевірка виконується шляхом перевірки окремих правил, доданих за допомогою addRule(), і подальшого виклику події onValidate. Її обробник може бути використаний для додаткової перевірки, зазвичай для перевірки правильності комбінації значень у кількох елементах форми.

Якщо виявлено помилку, вона передається у форму за допомогою методу addError(). Це може бути викликано як на певному елементі, так і безпосередньо на формі.

protected function createComponentSignInForm(): Form
{
	$form = new Form;
	// ...
	$form->onValidate[] = [$this, 'validateSignInForm'];
	return $form;
}

public function validateSignInForm(Form $form, \stdClass $data): void
{
	if ($data->foo > 1 && $data->bar > 5) {
		$form->addError('Ця комбінація неможлива.');
	}
}

Помилки обробки

У багатьох випадках ми виявляємо помилку, коли обробляємо дійсну форму, наприклад, коли ми записуємо новий запис у базу даних і стикаємося з дублюючим ключем. У цьому випадку ми передаємо помилку назад у форму за допомогою методу addError(). Це може бути викликано як на певному елементі, так і безпосередньо на формі:

try {
	$data = $form->getValues();
	$this->user->login($data->username, $data->password);
	$this->redirect('Home:');

} catch (Nette\Security\AuthenticationException $e) {
	if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) {
		$form->addError('Неверный пароль.');
	}
}

Якщо можливо, ми рекомендуємо додати помилку безпосередньо до елемента форми, оскільки вона відображатиметься поруч із ним під час використання рендерингу за замовчуванням.

$form['date']->addError('Извините, эта дата уже занята.');

Ви можете викликати addError() кілька разів, щоб передати кілька повідомлень про помилки формі або елементу. Ви отримуєте їх за допомогою функції getErrors().

Зверніть увагу, що $form->getErrors() повертає зведення всіх повідомлень про помилки, навіть тих, що були передані безпосередньо окремим елементам, а не тільки безпосередньо формі. Повідомлення про помилки, передані тільки формі, витягуються через $form->getOwnErrors().

Зміна вхідних значень

Використовуючи метод addFilter(), ми можемо змінити значення, введене користувачем. У цьому прикладі ми будемо допускати і видаляти пробіли в поштовому індексі:

$form->addText('zip', 'Поштовий індекс:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // видалити пробіли з поштового індексу
	})
	->addRule($form::Pattern, 'Поштовий індекс не складається з п'яти цифр', '\d{5}');

Фільтр увімкнено між правилами перевірки й умовами і тому він залежить від порядку проходження методів, тобто фільтр і правило викликаються в тому самому порядку, що й порядок проходження методів addFilter() і addRule().

Валідація JavaScript

Мова правил та умов перевірки є потужною. Незважаючи на те, що всі конструкції працюють як на стороні сервера, так і на стороні клієнта, у JavaScript. Правила передаються в HTML-атрибутах data-nette-rules у вигляді JSON. Сама валідація обробляється іншим скриптом, який перехоплює всі події форми submit, перебирає всі дані, що вводяться, і запускає відповідні валідації.

Цей скрипт – netteForms.js, який доступний з декількох можливих джерел:

Ви можете вбудувати сценарій безпосередньо в HTML-сторінку з CDN:

<script src="https://unpkg.com/nette-forms@3/src/assets/netteForms.js"></script>

Або скопіюйте локально в загальну папку проєкту (наприклад, із сайту vendor/nette/forms/src/assets/netteForms.min.js):

<script src="/path/to/netteForms.min.js"></script>

Або встановіть через npm:

npm install nette-forms

А потім завантажте та запустіть:

import netteForms from 'nette-forms';
netteForms.initOnLoad();

Крім того, ви можете завантажити його безпосередньо з папки vendor:

import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();

Динамічний JavaScript

Ви хочете відображати поля адреси тільки в тому випадку, якщо користувач вирішить відправити товар поштою? Немає проблем. Ключем є пара методів addCondition() і toggle():

$form->addCheckbox('send_it')
	->addCondition($form::Equal, true)
		->toggle('#address-container');

Цей код говорить, що при виконанні умови, тобто при встановленні прапорця, HTML-елемент #address-container стане видимим. І навпаки. Отже, ми поміщаємо елементи форми з адресою одержувача в контейнер із цим ID, і під час натискання на прапорець вони приховуються або показуються. Цим займається скрипт netteForms.js.

Будь-який селектор може бути переданий як аргумент методу toggle(). З історичних причин буквено-цифровий рядок без інших спеціальних символів розглядають як ідентифікатор елемента, так само як якби йому передував символ #. Второй необязательный параметр позволяет нам изменить поведение, т. е. если бы мы использовали toggle('#address-container', false), елемент відображали б тільки в разі знятого прапорця.

Реалізація JavaScript за замовчуванням змінює властивість hidden для елементів. Однак ми можемо легко змінити поведінку, наприклад, додавши анімацію. Просто перевизначте метод Nette.toggle у JavaScript за допомогою власного рішення:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// скрыть или показать 'el' в зависимости от значения 'visible'
	});
};

Вимкнення валідації

У деяких випадках необхідно відключити валідацію. Якщо кнопка submit не повинна виконувати перевірку після відправлення (наприклад, кнопка Скасування або Предварительный просмотр), ви можете відключити перевірку, викликавши $submit->setValidationScope([]). Ви також можете перевірити форму частково, вказавши елементи для перевірки.

$form->addText('name')
	->setRequired();

$details = $form->addContainer('details');
$details->addInteger('age')
	->setRequired('age');
$details->addInteger('age2')
	->setRequired('age2');

$form->addSubmit('send1'); // Перевіряє всю форму
$form->addSubmit('send2')
	->setValidationScope([]); // Нічого не підтверджує
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Перевіряє тільки поле 'ім'я'
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Перевіряється тільки поле 'вік'
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Перевіряє контейнер 'details'

Подія onValidate на формі викликається завжди і не залежить від setValidationScope. Подія onValidate на контейнері викликається тільки тоді, коли цей контейнер вказано для часткової валідації.