Nette Documentation Preview

syntax
Űrlap validáció
***************


Kötelező elemek
===============

A kötelező elemeket a `setRequired()` metódussal jelöljük meg, amelynek argumentuma a [#hibaüzenetek] hibaüzenet szövege, amely akkor jelenik meg, ha a felhasználó nem tölti ki az elemet. Ha nem adunk meg argumentumot, az alapértelmezett hibaüzenet kerül felhasználásra.

```php
$form->addText('name', 'Név:')
	->setRequired('Kérjük, adja meg a nevét');
```


Szabályok
=========

Validációs szabályokat az elemekhez az `addRule()` metódussal adunk hozzá. Az első paraméter a szabály, a második a [#hibaüzenetek] hibaüzenet szövege, a harmadik pedig a validációs szabály argumentuma.

```php
$form->addPassword('password', 'Jelszó:')
	->addRule($form::MinLength, 'A jelszónak legalább %d karakter hosszúnak kell lennie', 8);
```

**A validációs szabályok csak akkor kerülnek ellenőrzésre, ha a felhasználó kitöltötte az elemet.**

A Nette számos előre definiált szabállyal rendelkezik, amelyek nevei a `Nette\Forms\Form` osztály konstansai. Minden elemhez használhatjuk ezeket a szabályokat:

| konstans | leírás | argumentum típusa
|-------
| `Required` | kötelező elem, alias a `setRequired()` számára | -
| `Filled` | kötelező elem, alias a `setRequired()` számára | -
| `Blank` | az elem nem lehet kitöltve | -
| `Equal` | az érték megegyezik a paraméterrel | `mixed`
| `NotEqual` | az érték nem egyezik meg a paraméterrel | `mixed`
| `IsIn` | az érték megegyezik a tömb valamelyik elemével | `array`
| `IsNotIn` | az érték nem egyezik meg a tömb egyik elemével sem | `array`
| `Valid` | az elem helyesen van kitöltve? ([#feltételek] számára) | -


Szöveges bevitelek
------------------

Az `addText()`, `addPassword()`, `addTextArea()`, `addEmail()`, `addInteger()`, `addFloat()` elemekhez használhatók a következő szabályok is:

| `MinLength` | minimális szöveghossz | `int`
| `MaxLength` | maximális szöveghossz | `int`
| `Length` | hossz tartományban vagy pontos hossz | pár `[int, int]` vagy `int`
| `Email` | érvényes e-mail cím | -
| `URL` | abszolút URL | -
| `Pattern` | megfelel a reguláris kifejezésnek | `string`
| `PatternInsensitive` | mint a `Pattern`, de kis- és nagybetű érzéketlen | `string`
| `Integer` | egész szám érték | -
| `Numeric` | alias az `Integer` számára | -
| `Float` | szám | -
| `Min` | numerikus elem minimális értéke | `int\|float`
| `Max` | numerikus elem maximális értéke | `int\|float`
| `Range` | érték tartományban | pár `[int\|float, int\|float]`

Az `Integer`, `Numeric` és `Float` validációs szabályok azonnal átalakítják az értéket integerre, illetve floatra. Továbbá az `URL` szabály elfogadja a séma nélküli címet is (pl. `nette.org`), és kiegészíti a sémát (`https://nette.org`). A `Pattern` és `PatternIcase` kifejezésnek az egész értékre kell érvényesnek lennie, azaz mintha `^` és `$` karakterekkel lenne körbevéve.


Elemek száma
------------

Az `addMultiUpload()`, `addCheckboxList()`, `addMultiSelect()` elemekhez használhatók a következő szabályok is a kiválasztott elemek, illetve feltöltött fájlok számának korlátozására:

| `MinLength` | minimális szám | `int`
| `MaxLength` | maximális szám | `int`
| `Length` | szám tartományban vagy pontos szám | pár `[int, int]` vagy `int`


Fájlfeltöltések
---------------

Az `addUpload()`, `addMultiUpload()` elemekhez használhatók a következő szabályok is:

| `MaxFileSize` | maximális fájlméret bájtban | `int`
| `MimeType` | MIME típus, helyettesítő karakterek engedélyezettek (`'video/*'`) | `string\|string[]`
| `Image` | JPEG, PNG, GIF, WebP, AVIF kép | -
| `Pattern` | a fájlnév megfelel a reguláris kifejezésnek | `string`
| `PatternInsensitive` | mint a `Pattern`, de kis- és nagybetű érzéketlen | `string`

A `MimeType` és `Image` szabályokhoz szükség van a `fileinfo` PHP kiterjesztésre. Azt, hogy a fájl vagy kép a kívánt típusú-e, az aláírása alapján észlelik, és **nem ellenőrzik az egész fájl integritását.** Azt, hogy a kép nem sérült-e, például a [betöltésével |http:request#toImage] lehet megállapítani.


Hibaüzenetek
============

Minden előre definiált szabálynak, kivéve a `Pattern` és `PatternInsensitive` szabályokat, van alapértelmezett hibaüzenete, így azt el lehet hagyni. Azonban az összes üzenet testreszabott megadásával és megfogalmazásával felhasználóbarátabbá teheti az űrlapot.

Az alapértelmezett üzeneteket megváltoztathatja a [konfigurációban|forms:configuration], a `Nette\Forms\Validator::$messages` tömb szövegeinek módosításával, vagy a [fordító |rendering#Fordítás] használatával.

A hibaüzenetek szövegében a következő helyettesítő stringek használhatók:

| `%d`     | sorban helyettesíti a szabály argumentumaival
| `%n$d`   | helyettesíti a szabály n-edik argumentumával
| `%label` | helyettesíti az elem címkéjével (kettőspont nélkül)
| `%name`  | helyettesíti az elem nevével (pl. `name`)
| `%value` | helyettesíti a felhasználó által beírt értékkel

```php
$form->addText('name', 'Név:')
	->setRequired('Kérjük, töltse ki a %label mezőt');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'legalább %d és legfeljebb %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'legfeljebb %2$d és legalább %1$d', [5, 10]);
```


Feltételek
==========

A szabályokon kívül feltételeket is hozzáadhatunk. Ezeket hasonlóan írjuk, mint a szabályokat, csak az `addRule()` helyett az `addCondition()` metódust használjuk, és természetesen nem adunk meg hibaüzenetet (a feltétel csak kérdez):

```php
$form->addPassword('password', 'Jelszó:')
	// ha a jelszó nem hosszabb 8 karakternél
	->addCondition($form::MaxLength, 8)
		// akkor számjegyet kell tartalmaznia
		->addRule($form::Pattern, 'Számjegyet kell tartalmaznia', '.*[0-9].*');
```

A feltételt az aktuális elemen kívül más elemhez is köthetjük az `addConditionOn()` segítségével. Első paraméterként az elemre való hivatkozást adjuk meg. Ebben a példában az e-mail csak akkor lesz kötelező, ha a checkbox be van jelölve (az értéke true lesz):

```php
$form->addCheckbox('newsletters', 'küldjenek nekem hírleveleket');

$form->addEmail('email', 'E-mail:')
	// ha a checkbox be van jelölve
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// akkor követelje meg az e-mailt
		->setRequired('Adja meg az e-mail címét');
```

A feltételekből komplex struktúrákat hozhatunk létre az `elseCondition()` és `endCondition()` segítségével:

```php
$form->addText(/* ... */)
	->addCondition(/* ... */) // ha az első feltétel teljesül
		->addConditionOn(/* ... */) // és a második feltétel egy másik elemen
			->addRule(/* ... */) // követelje meg ezt a szabályt
		->elseCondition() // ha a második feltétel nem teljesül
			->addRule(/* ... */) // követelje meg ezeket a szabályokat
			->addRule(/* ... */)
		->endCondition() // visszatérünk az első feltételhez
		->addRule(/* ... */);
```

A Nette-ben nagyon könnyen reagálhatunk a feltétel teljesülésére vagy nem teljesülésére JavaScript oldalon is a `toggle()` metódus segítségével, lásd [#dinamikus JavaScript].


Hivatkozás más elemre
=====================

Szabály vagy feltétel argumentumaként más űrlap elemet is átadhatunk. A szabály ezután a felhasználó által később a böngészőben beírt értéket használja. Így például dinamikusan validálhatjuk, hogy a `password` elem ugyanazt a stringet tartalmazza-e, mint a `password_confirm` elem:

```php
$form->addPassword('password', 'Jelszó');
$form->addPassword('password_confirm', 'Jelszó megerősítése')
    ->addRule($form::Equal, 'A megadott jelszavak nem egyeznek', $form['password']);
```


Egyéni szabályok és feltételek
==============================

Néha olyan helyzetbe kerülünk, amikor a Nette beépített validációs szabályai nem elegendőek, és a felhasználótól származó adatokat a saját módunkon kell validálnunk. A Nette-ben ez nagyon egyszerű!

Az `addRule()` vagy `addCondition()` metódusoknak első paraméterként tetszőleges callbacket adhatunk át. Ez első paraméterként magát az elemet kapja, és boolean értéket ad vissza, amely meghatározza, hogy a validáció rendben lezajlott-e. Az `addRule()` segítségével történő szabály hozzáadásakor további argumentumokat is megadhatunk, ezeket aztán második paraméterként adjuk át.

Így létrehozhatunk egy saját validátor készletet osztályként statikus metódusokkal:

```php
class MyValidators
{
	// teszteli, hogy az érték osztható-e az argumentummal
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// további validátorok
	}
}
```

A használat ezután nagyon egyszerű:

```php
$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'Az értéknek a %d szám többszörösének kell lennie',
		8,
	);
```

Egyéni validációs szabályokat JavaScripthez is hozzáadhatunk. A feltétel az, hogy a szabály statikus metódus legyen. A neve a JavaScript validátor számára az osztály nevének a visszaperjelek `\` nélküli, aláhúzásjel `_` és a metódus nevének összekapcsolásával jön létre. Pl. az `App\MyValidators::validateDivisibility`-t `AppMyValidators_validateDivisibility`-ként írjuk, és hozzáadjuk a `Nette.validators` objektumhoz:

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


onValidate esemény
==================

Az űrlap elküldése után validáció történik, amely során ellenőrzésre kerülnek az `addRule()` segítségével hozzáadott egyes szabályok, majd kiváltódik az [esemény |nette:glossary#Eventek események] `onValidate`. Ennek a kezelőjét (handler) kiegészítő validációra használhatjuk, tipikusan az értékek helyes kombinációjának ellenőrzésére több űrlap elemben.

Ha hibát észlelünk, azt az `addError()` metódussal adjuk át az űrlapnak. Ezt vagy egy konkrét elemen, vagy közvetlenül az űrlapon hívhatjuk meg.

```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('Ez a kombináció nem lehetséges.');
	}
}
```


Hibák a feldolgozás során
=========================

Sok esetben csak akkor értesülünk a hibáról, amikor az érvényes űrlapot dolgozzuk fel, például új elemet írunk az adatbázisba, és kulcsduplikációba ütközünk. Ebben az esetben a hibát ismét az `addError()` metódussal adjuk át az űrlapnak. Ezt vagy egy konkrét elemen, vagy közvetlenül az űrlapon hívhatjuk meg:

```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('Érvénytelen jelszó.');
	}
}
```

Ha lehetséges, javasoljuk, hogy a hibát közvetlenül az űrlap eleméhez csatolja, mert az alapértelmezett renderer használatakor mellette jelenik meg.

```php
$form['date']->addError('Elnézést, de ez a dátum már foglalt.');
```

Az `addError()` metódust ismételten meghívhatja, és így több hibaüzenetet adhat át az űrlapnak vagy elemnek. Ezeket a `getErrors()` segítségével szerezheti meg.

Figyelem, a `$form->getErrors()` az összes hibaüzenet összegzését adja vissza, beleértve azokat is, amelyeket közvetlenül az egyes elemekhez adtak át, nem csak közvetlenül az űrlaphoz. A csak az űrlaphoz átadott hibaüzeneteket a `$form->getOwnErrors()` segítségével szerezheti meg.


Bemenet módosítása
==================

Az `addFilter()` metódus segítségével módosíthatjuk a felhasználó által beírt értéket. Ebben a példában toleráljuk és eltávolítjuk a szóközöket az irányítószámban:

```php
$form->addText('zip', 'Irányítószám:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // eltávolítjuk a szóközöket az irányítószámból
	})
	->addRule($form::Pattern, 'Az irányítószám nem öt számjegyű', '\d{5}');
```

A szűrő beépül a validációs szabályok és feltételek közé, tehát a metódusok sorrendje számít, azaz a szűrő és a szabály abban a sorrendben hívódik meg, ahogy az `addFilter()` és `addRule()` metódusok sorrendje van.


JavaScript validáció
====================

A feltételek és szabályok megfogalmazásának nyelve nagyon erős. Minden konstrukció működik mind a szerveroldalon, mind a JavaScript oldalon. HTML attribútumokban `data-nette-rules` JSON formátumban kerülnek átadásra. Magát a validációt pedig egy szkript végzi, amely elfogja az űrlap `submit` eseményét, végigmegy az egyes elemeken, és végrehajtja a megfelelő validációt.

Ez a szkript a `netteForms.js`, és több lehetséges forrásból érhető el:

A szkriptet közvetlenül beillesztheti a HTML oldalba egy CDN-ről:

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

Vagy másolja helyileg a projekt nyilvános mappájába (pl. a `vendor/nette/forms/src/assets/netteForms.min.js` fájlból):

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

Vagy telepítse [npm|https://www.npmjs.com/package/nette-forms] segítségével:

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

Majd töltse be és futtassa:

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

Alternatívaként betöltheti közvetlenül a `vendor` mappából:

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


Dinamikus JavaScript
====================

Szeretné megjeleníteni a cím megadására szolgáló mezőket csak akkor, ha a felhasználó postai kézbesítést választ? Semmi probléma. A kulcs az `addCondition()` & `toggle()` metóduspár:

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

Ez a kód azt mondja, hogy amikor a feltétel teljesül, tehát amikor a checkbox be van jelölve, a `#address-container` HTML elem látható lesz. És fordítva. A címzett címét tartalmazó űrlap elemeket tehát egy ilyen ID-jű konténerbe helyezzük, és a checkboxra kattintva elrejtődnek vagy megjelennek. Ezt a `netteForms.js` szkript biztosítja.

A `toggle()` metódus argumentumaként tetszőleges selectort adhatunk át. Történelmi okokból az alfanumerikus string további speciális karakterek nélkül elem ID-ként értelmeződik, tehát ugyanúgy, mintha `#` karakter előzné meg. A második, nem kötelező paraméter lehetővé teszi a viselkedés megfordítását, azaz ha a `toggle('#address-container', false)`-t használnánk, az elem éppen ellenkezőleg, csak akkor jelenne meg, ha a checkbox nem lenne bejelölve.

Az alapértelmezett implementáció JavaScriptben az elemek `hidden` propertyjét változtatja meg. A viselkedést azonban könnyen megváltoztathatjuk, például animációt adhatunk hozzá. Elég JavaScriptben felülírni a `Nette.toggle` metódust saját megoldással:

```js
Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// elrejtjük vagy megjelenítjük az 'el'-t a 'visible' értékétől függően
	});
};
```


Validáció kikapcsolása
======================

Néha hasznos lehet a validáció kikapcsolása. Ha a küldés gomb megnyomása nem kell, hogy validációt végezzen (alkalmas *Cancel* vagy *Preview* gombokhoz), kikapcsoljuk a `$submit->setValidationScope([])` metódussal. Ha csak részleges validációt kell végeznie, megadhatjuk, mely mezők vagy űrlap konténerek validálódjanak.

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

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

$form->addSubmit('send1'); // Az egész űrlapot validálja
$form->addSubmit('send2')
	->setValidationScope([]); // Egyáltalán nem validál
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Csak a name elemet validálja
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Csak az age elemet validálja
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // A details konténert validálja
```

A `setValidationScope` nem befolyásolja a [#onValidate esemény] eseményt az űrlapon, amely mindig meghívásra kerül. A konténer `onValidate` eseménye csak akkor kerül kiváltásra, ha ez a konténer részleges validációra van megjelölve.

Űrlap validáció

Kötelező elemek

A kötelező elemeket a setRequired() metódussal jelöljük meg, amelynek argumentuma a hibaüzenetek hibaüzenet szövege, amely akkor jelenik meg, ha a felhasználó nem tölti ki az elemet. Ha nem adunk meg argumentumot, az alapértelmezett hibaüzenet kerül felhasználásra.

$form->addText('name', 'Név:')
	->setRequired('Kérjük, adja meg a nevét');

Szabályok

Validációs szabályokat az elemekhez az addRule() metódussal adunk hozzá. Az első paraméter a szabály, a második a hibaüzenetek hibaüzenet szövege, a harmadik pedig a validációs szabály argumentuma.

$form->addPassword('password', 'Jelszó:')
	->addRule($form::MinLength, 'A jelszónak legalább %d karakter hosszúnak kell lennie', 8);

A validációs szabályok csak akkor kerülnek ellenőrzésre, ha a felhasználó kitöltötte az elemet.

A Nette számos előre definiált szabállyal rendelkezik, amelyek nevei a Nette\Forms\Form osztály konstansai. Minden elemhez használhatjuk ezeket a szabályokat:

konstans leírás argumentum típusa
Required kötelező elem, alias a setRequired() számára
Filled kötelező elem, alias a setRequired() számára
Blank az elem nem lehet kitöltve
Equal az érték megegyezik a paraméterrel mixed
NotEqual az érték nem egyezik meg a paraméterrel mixed
IsIn az érték megegyezik a tömb valamelyik elemével array
IsNotIn az érték nem egyezik meg a tömb egyik elemével sem array
Valid az elem helyesen van kitöltve? (feltételek számára)

Szöveges bevitelek

Az addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() elemekhez használhatók a következő szabályok is:

MinLength minimális szöveghossz int
MaxLength maximális szöveghossz int
Length hossz tartományban vagy pontos hossz pár [int, int] vagy int
Email érvényes e-mail cím
URL abszolút URL
Pattern megfelel a reguláris kifejezésnek string
PatternInsensitive mint a Pattern, de kis- és nagybetű érzéketlen string
Integer egész szám érték
Numeric alias az Integer számára
Float szám
Min numerikus elem minimális értéke int|float
Max numerikus elem maximális értéke int|float
Range érték tartományban pár [int|float, int|float]

Az Integer, Numeric és Float validációs szabályok azonnal átalakítják az értéket integerre, illetve floatra. Továbbá az URL szabály elfogadja a séma nélküli címet is (pl. nette.org), és kiegészíti a sémát (https://nette.org). A Pattern és PatternIcase kifejezésnek az egész értékre kell érvényesnek lennie, azaz mintha ^ és $ karakterekkel lenne körbevéve.

Elemek száma

Az addMultiUpload(), addCheckboxList(), addMultiSelect() elemekhez használhatók a következő szabályok is a kiválasztott elemek, illetve feltöltött fájlok számának korlátozására:

MinLength minimális szám int
MaxLength maximális szám int
Length szám tartományban vagy pontos szám pár [int, int] vagy int

Fájlfeltöltések

Az addUpload(), addMultiUpload() elemekhez használhatók a következő szabályok is:

MaxFileSize maximális fájlméret bájtban int
MimeType MIME típus, helyettesítő karakterek engedélyezettek ('video/*') string|string[]
Image JPEG, PNG, GIF, WebP, AVIF kép
Pattern a fájlnév megfelel a reguláris kifejezésnek string
PatternInsensitive mint a Pattern, de kis- és nagybetű érzéketlen string

A MimeType és Image szabályokhoz szükség van a fileinfo PHP kiterjesztésre. Azt, hogy a fájl vagy kép a kívánt típusú-e, az aláírása alapján észlelik, és nem ellenőrzik az egész fájl integritását. Azt, hogy a kép nem sérült-e, például a betöltésével lehet megállapítani.

Hibaüzenetek

Minden előre definiált szabálynak, kivéve a Pattern és PatternInsensitive szabályokat, van alapértelmezett hibaüzenete, így azt el lehet hagyni. Azonban az összes üzenet testreszabott megadásával és megfogalmazásával felhasználóbarátabbá teheti az űrlapot.

Az alapértelmezett üzeneteket megváltoztathatja a konfigurációban, a Nette\Forms\Validator::$messages tömb szövegeinek módosításával, vagy a fordító használatával.

A hibaüzenetek szövegében a következő helyettesítő stringek használhatók:

%d sorban helyettesíti a szabály argumentumaival
%n$d helyettesíti a szabály n-edik argumentumával
%label helyettesíti az elem címkéjével (kettőspont nélkül)
%name helyettesíti az elem nevével (pl. name)
%value helyettesíti a felhasználó által beírt értékkel
$form->addText('name', 'Név:')
	->setRequired('Kérjük, töltse ki a %label mezőt');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'legalább %d és legfeljebb %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'legfeljebb %2$d és legalább %1$d', [5, 10]);

Feltételek

A szabályokon kívül feltételeket is hozzáadhatunk. Ezeket hasonlóan írjuk, mint a szabályokat, csak az addRule() helyett az addCondition() metódust használjuk, és természetesen nem adunk meg hibaüzenetet (a feltétel csak kérdez):

$form->addPassword('password', 'Jelszó:')
	// ha a jelszó nem hosszabb 8 karakternél
	->addCondition($form::MaxLength, 8)
		// akkor számjegyet kell tartalmaznia
		->addRule($form::Pattern, 'Számjegyet kell tartalmaznia', '.*[0-9].*');

A feltételt az aktuális elemen kívül más elemhez is köthetjük az addConditionOn() segítségével. Első paraméterként az elemre való hivatkozást adjuk meg. Ebben a példában az e-mail csak akkor lesz kötelező, ha a checkbox be van jelölve (az értéke true lesz):

$form->addCheckbox('newsletters', 'küldjenek nekem hírleveleket');

$form->addEmail('email', 'E-mail:')
	// ha a checkbox be van jelölve
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// akkor követelje meg az e-mailt
		->setRequired('Adja meg az e-mail címét');

A feltételekből komplex struktúrákat hozhatunk létre az elseCondition() és endCondition() segítségével:

$form->addText(/* ... */)
	->addCondition(/* ... */) // ha az első feltétel teljesül
		->addConditionOn(/* ... */) // és a második feltétel egy másik elemen
			->addRule(/* ... */) // követelje meg ezt a szabályt
		->elseCondition() // ha a második feltétel nem teljesül
			->addRule(/* ... */) // követelje meg ezeket a szabályokat
			->addRule(/* ... */)
		->endCondition() // visszatérünk az első feltételhez
		->addRule(/* ... */);

A Nette-ben nagyon könnyen reagálhatunk a feltétel teljesülésére vagy nem teljesülésére JavaScript oldalon is a toggle() metódus segítségével, lásd dinamikus JavaScript.

Hivatkozás más elemre

Szabály vagy feltétel argumentumaként más űrlap elemet is átadhatunk. A szabály ezután a felhasználó által később a böngészőben beírt értéket használja. Így például dinamikusan validálhatjuk, hogy a password elem ugyanazt a stringet tartalmazza-e, mint a password_confirm elem:

$form->addPassword('password', 'Jelszó');
$form->addPassword('password_confirm', 'Jelszó megerősítése')
    ->addRule($form::Equal, 'A megadott jelszavak nem egyeznek', $form['password']);

Egyéni szabályok és feltételek

Néha olyan helyzetbe kerülünk, amikor a Nette beépített validációs szabályai nem elegendőek, és a felhasználótól származó adatokat a saját módunkon kell validálnunk. A Nette-ben ez nagyon egyszerű!

Az addRule() vagy addCondition() metódusoknak első paraméterként tetszőleges callbacket adhatunk át. Ez első paraméterként magát az elemet kapja, és boolean értéket ad vissza, amely meghatározza, hogy a validáció rendben lezajlott-e. Az addRule() segítségével történő szabály hozzáadásakor további argumentumokat is megadhatunk, ezeket aztán második paraméterként adjuk át.

Így létrehozhatunk egy saját validátor készletet osztályként statikus metódusokkal:

class MyValidators
{
	// teszteli, hogy az érték osztható-e az argumentummal
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// további validátorok
	}
}

A használat ezután nagyon egyszerű:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'Az értéknek a %d szám többszörösének kell lennie',
		8,
	);

Egyéni validációs szabályokat JavaScripthez is hozzáadhatunk. A feltétel az, hogy a szabály statikus metódus legyen. A neve a JavaScript validátor számára az osztály nevének a visszaperjelek \ nélküli, aláhúzásjel _ és a metódus nevének összekapcsolásával jön létre. Pl. az App\MyValidators::validateDivisibility-t AppMyValidators_validateDivisibility-ként írjuk, és hozzáadjuk a Nette.validators objektumhoz:

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

onValidate esemény

Az űrlap elküldése után validáció történik, amely során ellenőrzésre kerülnek az addRule() segítségével hozzáadott egyes szabályok, majd kiváltódik az esemény onValidate. Ennek a kezelőjét (handler) kiegészítő validációra használhatjuk, tipikusan az értékek helyes kombinációjának ellenőrzésére több űrlap elemben.

Ha hibát észlelünk, azt az addError() metódussal adjuk át az űrlapnak. Ezt vagy egy konkrét elemen, vagy közvetlenül az űrlapon hívhatjuk meg.

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('Ez a kombináció nem lehetséges.');
	}
}

Hibák a feldolgozás során

Sok esetben csak akkor értesülünk a hibáról, amikor az érvényes űrlapot dolgozzuk fel, például új elemet írunk az adatbázisba, és kulcsduplikációba ütközünk. Ebben az esetben a hibát ismét az addError() metódussal adjuk át az űrlapnak. Ezt vagy egy konkrét elemen, vagy közvetlenül az űrlapon hívhatjuk meg:

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('Érvénytelen jelszó.');
	}
}

Ha lehetséges, javasoljuk, hogy a hibát közvetlenül az űrlap eleméhez csatolja, mert az alapértelmezett renderer használatakor mellette jelenik meg.

$form['date']->addError('Elnézést, de ez a dátum már foglalt.');

Az addError() metódust ismételten meghívhatja, és így több hibaüzenetet adhat át az űrlapnak vagy elemnek. Ezeket a getErrors() segítségével szerezheti meg.

Figyelem, a $form->getErrors() az összes hibaüzenet összegzését adja vissza, beleértve azokat is, amelyeket közvetlenül az egyes elemekhez adtak át, nem csak közvetlenül az űrlaphoz. A csak az űrlaphoz átadott hibaüzeneteket a $form->getOwnErrors() segítségével szerezheti meg.

Bemenet módosítása

Az addFilter() metódus segítségével módosíthatjuk a felhasználó által beírt értéket. Ebben a példában toleráljuk és eltávolítjuk a szóközöket az irányítószámban:

$form->addText('zip', 'Irányítószám:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // eltávolítjuk a szóközöket az irányítószámból
	})
	->addRule($form::Pattern, 'Az irányítószám nem öt számjegyű', '\d{5}');

A szűrő beépül a validációs szabályok és feltételek közé, tehát a metódusok sorrendje számít, azaz a szűrő és a szabály abban a sorrendben hívódik meg, ahogy az addFilter() és addRule() metódusok sorrendje van.

JavaScript validáció

A feltételek és szabályok megfogalmazásának nyelve nagyon erős. Minden konstrukció működik mind a szerveroldalon, mind a JavaScript oldalon. HTML attribútumokban data-nette-rules JSON formátumban kerülnek átadásra. Magát a validációt pedig egy szkript végzi, amely elfogja az űrlap submit eseményét, végigmegy az egyes elemeken, és végrehajtja a megfelelő validációt.

Ez a szkript a netteForms.js, és több lehetséges forrásból érhető el:

A szkriptet közvetlenül beillesztheti a HTML oldalba egy CDN-ről:

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

Vagy másolja helyileg a projekt nyilvános mappájába (pl. a vendor/nette/forms/src/assets/netteForms.min.js fájlból):

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

Vagy telepítse npm segítségével:

npm install nette-forms

Majd töltse be és futtassa:

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

Alternatívaként betöltheti közvetlenül a vendor mappából:

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

Dinamikus JavaScript

Szeretné megjeleníteni a cím megadására szolgáló mezőket csak akkor, ha a felhasználó postai kézbesítést választ? Semmi probléma. A kulcs az addCondition() & toggle() metóduspár:

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

Ez a kód azt mondja, hogy amikor a feltétel teljesül, tehát amikor a checkbox be van jelölve, a #address-container HTML elem látható lesz. És fordítva. A címzett címét tartalmazó űrlap elemeket tehát egy ilyen ID-jű konténerbe helyezzük, és a checkboxra kattintva elrejtődnek vagy megjelennek. Ezt a netteForms.js szkript biztosítja.

A toggle() metódus argumentumaként tetszőleges selectort adhatunk át. Történelmi okokból az alfanumerikus string további speciális karakterek nélkül elem ID-ként értelmeződik, tehát ugyanúgy, mintha # karakter előzné meg. A második, nem kötelező paraméter lehetővé teszi a viselkedés megfordítását, azaz ha a toggle('#address-container', false)-t használnánk, az elem éppen ellenkezőleg, csak akkor jelenne meg, ha a checkbox nem lenne bejelölve.

Az alapértelmezett implementáció JavaScriptben az elemek hidden propertyjét változtatja meg. A viselkedést azonban könnyen megváltoztathatjuk, például animációt adhatunk hozzá. Elég JavaScriptben felülírni a Nette.toggle metódust saját megoldással:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// elrejtjük vagy megjelenítjük az 'el'-t a 'visible' értékétől függően
	});
};

Validáció kikapcsolása

Néha hasznos lehet a validáció kikapcsolása. Ha a küldés gomb megnyomása nem kell, hogy validációt végezzen (alkalmas Cancel vagy Preview gombokhoz), kikapcsoljuk a $submit->setValidationScope([]) metódussal. Ha csak részleges validációt kell végeznie, megadhatjuk, mely mezők vagy űrlap konténerek validálódjanak.

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

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

$form->addSubmit('send1'); // Az egész űrlapot validálja
$form->addSubmit('send2')
	->setValidationScope([]); // Egyáltalán nem validál
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Csak a name elemet validálja
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Csak az age elemet validálja
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // A details konténert validálja

A setValidationScope nem befolyásolja a onValidate esemény eseményt az űrlapon, amely mindig meghívásra kerül. A konténer onValidate eseménye csak akkor kerül kiváltásra, ha ez a konténer részleges validációra van megjelölve.