Nette Documentation Preview

syntax
Validación de formularios
*************************


Elementos obligatorios
======================

Marcamos los elementos obligatorios con el método `setRequired()`, cuyo argumento es el texto del [mensaje de error |#Mensajes de error] que se mostrará si el usuario no rellena el elemento. Si no se proporciona el argumento, se utilizará el mensaje de error predeterminado.

```php
$form->addText('name', 'Nombre:')
	->setRequired('Por favor, introduzca su nombre');
```


Reglas
======

Añadimos reglas de validación a los elementos con el método `addRule()`. El primer parámetro es la regla, el segundo es el texto del [mensaje de error |#Mensajes de error] y el tercero es el argumento de la regla de validación.

```php
$form->addPassword('password', 'Contraseña:')
	->addRule($form::MinLength, 'La contraseña debe tener al menos %d caracteres', 8);
```

**Las reglas de validación solo se verifican si el usuario ha rellenado el elemento.**

Nette viene con una serie de reglas predefinidas, cuyos nombres son constantes de la clase `Nette\Forms\Form`. Podemos usar estas reglas para todos los elementos:

| constante | descripción | tipo de argumento
|-------
| `Required` | elemento obligatorio, alias para `setRequired()` | -
| `Filled` | elemento obligatorio, alias para `setRequired()` | -
| `Blank` | el elemento no debe ser rellenado | -
| `Equal` | el valor es igual al parámetro | `mixed`
| `NotEqual` | el valor no es igual al parámetro | `mixed`
| `IsIn` | el valor es igual a uno de los elementos del array | `array`
| `IsNotIn` | el valor no es igual a ninguno de los elementos del array | `array`
| `Valid` | ¿está el elemento rellenado correctamente? (para [#condiciones]) | -


Entradas de texto
-----------------

Para los elementos `addText()`, `addPassword()`, `addTextArea()`, `addEmail()`, `addInteger()`, `addFloat()`, también se pueden usar algunas de las siguientes reglas:

| `MinLength` | longitud mínima del texto | `int`
| `MaxLength` | longitud máxima del texto | `int`
| `Length` | longitud en un rango o longitud exacta | par `[int, int]` o `int`
| `Email` | dirección de correo electrónico válida | -
| `URL` | URL absoluta | -
| `Pattern` | coincide con la expresión regular | `string`
| `PatternInsensitive` | como `Pattern`, pero insensible a mayúsculas/minúsculas | `string`
| `Integer` | valor entero | -
| `Numeric` | alias para `Integer` | -
| `Float` | número | -
| `Min` | valor mínimo del elemento numérico | `int\|float`
| `Max` | valor máximo del elemento numérico | `int\|float`
| `Range` | valor en el rango | par `[int\|float, int\|float]`

Las reglas de validación `Integer`, `Numeric` y `Float` convierten directamente el valor a entero o flotante, respectivamente. Además, la regla `URL` también acepta una dirección sin esquema (p. ej., `nette.org`) y añade el esquema (`https://nette.org`). La expresión en `Pattern` y `PatternIcase` debe aplicarse a todo el valor, es decir, como si estuviera envuelta en los caracteres `^` y `$`.


Número de elementos
-------------------

Para los elementos `addMultiUpload()`, `addCheckboxList()`, `addMultiSelect()`, también se pueden usar las siguientes reglas para limitar el número de elementos seleccionados o archivos cargados:

| `MinLength` | número mínimo | `int`
| `MaxLength` | número máximo | `int`
| `Length` | número en un rango o número exacto | par `[int, int]` o `int`


Carga de archivos
-----------------

Para los elementos `addUpload()`, `addMultiUpload()`, también se pueden usar las siguientes reglas:

| `MaxFileSize` | tamaño máximo del archivo en bytes | `int`
| `MimeType` | Tipo MIME, se permiten comodines (`'video/*'`) | `string\|string[]`
| `Image` | imagen JPEG, PNG, GIF, WebP, AVIF | -
| `Pattern` | el nombre del archivo coincide con la expresión regular | `string`
| `PatternInsensitive` | como `Pattern`, pero insensible a mayúsculas/minúsculas | `string`

`MimeType` e `Image` requieren la extensión PHP `fileinfo`. Detectan si el archivo o imagen es del tipo requerido basándose en su firma y **no verifican la integridad de todo el archivo.** Si una imagen está dañada se puede detectar, por ejemplo, intentando [cargarla |http:request#toImage].


Mensajes de error
=================

Todas las reglas predefinidas, excepto `Pattern` y `PatternInsensitive`, tienen un mensaje de error predeterminado, por lo que se puede omitir. Sin embargo, al proporcionar y formular todos los mensajes a medida, hará que el formulario sea más fácil de usar.

Puede cambiar los mensajes predeterminados en la [configuración|forms:configuration], editando los textos en el array `Nette\Forms\Validator::$messages` o usando un [traductor |rendering#Traducción].

En el texto de los mensajes de error se pueden usar estas cadenas de marcador de posición:

| `%d`     | reemplaza secuencialmente con los argumentos de la regla
| `%n$d`   | reemplaza con el n-ésimo argumento de la regla
| `%label` | reemplaza con la etiqueta del elemento (sin dos puntos)
| `%name`  | reemplaza con el nombre del elemento (p. ej. `name`)
| `%value` | reemplaza con el valor introducido por el usuario

```php
$form->addText('name', 'Nombre:')
	->setRequired('Por favor, rellene %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'al menos %d y como máximo %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'como máximo %2$d y al menos %1$d', [5, 10]);
```


Condiciones
===========

Además de las reglas, también se pueden añadir condiciones. Se escriben de forma similar a las reglas, solo que en lugar de `addRule()` usamos el método `addCondition()` y, por supuesto, no proporcionamos ningún mensaje de error (la condición solo pregunta):

```php
$form->addPassword('password', 'Contraseña:')
	// si la contraseña no tiene más de 8 caracteres
	->addCondition($form::MaxLength, 8)
		// entonces debe contener un dígito
		->addRule($form::Pattern, 'Debe contener un dígito', '.*[0-9].*');
```

La condición también se puede vincular a un elemento diferente al actual usando `addConditionOn()`. Como primer parámetro, proporcionamos una referencia al elemento. En este ejemplo, el correo electrónico será obligatorio solo si se marca la casilla de verificación (su valor será `true`):

```php
$form->addCheckbox('newsletters', 'Enviarme boletines');

$form->addEmail('email', 'E-mail:')
	// si la casilla de verificación está marcada
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// entonces requiere el correo electrónico
		->setRequired('Introduzca una dirección de correo electrónico');
```

Se pueden crear estructuras complejas a partir de condiciones usando `elseCondition()` y `endCondition()`:

```php
$form->addText(/* ... */)
	->addCondition(/* ... */) // si se cumple la primera condición
		->addConditionOn(/* ... */) // y la segunda condición en otro elemento
			->addRule(/* ... */) // requiere esta regla
		->elseCondition() // si la segunda condición no se cumple
			->addRule(/* ... */) // requiere estas reglas
			->addRule(/* ... */)
		->endCondition() // volvemos a la primera condición
		->addRule(/* ... */);
```

En Nette, es muy fácil reaccionar al cumplimiento o incumplimiento de una condición también en el lado de JavaScript usando el método `toggle()`, consulte [#JavaScript dinámico].


Referencia a otro elemento
==========================

También se puede pasar otro elemento del formulario como argumento de una regla o condición. La regla entonces usará el valor introducido posteriormente por el usuario en el navegador. De esta manera, se puede validar dinámicamente, por ejemplo, que el elemento `password` contenga la misma cadena que el elemento `password_confirm`:

```php
$form->addPassword('password', 'Contraseña');
$form->addPassword('password_confirm', 'Confirmar contraseña')
    ->addRule($form::Equal, 'Las contraseñas introducidas no coinciden', $form['password']);
```


Reglas y condiciones personalizadas
===================================

A veces nos encontramos en una situación en la que las reglas de validación incorporadas en Nette no son suficientes y necesitamos validar los datos del usuario a nuestra manera. ¡En Nette esto es muy simple!

Se puede pasar cualquier callback como primer parámetro a los métodos `addRule()` o `addCondition()`. Este recibe el propio elemento como primer parámetro y devuelve un valor booleano que indica si la validación se realizó correctamente. Al añadir una regla con `addRule()`, también es posible especificar argumentos adicionales, que luego se pasan como segundo parámetro.

Así podemos crear nuestro propio conjunto de validadores como una clase con métodos estáticos:

```php
class MyValidators
{
	// comprueba si el valor es divisible por el argumento
	public static function validateDivisibility(Nette\Forms\Control $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(Nette\Forms\Control $input, $domain)
	{
		// otros validadores
	}
}
```

El uso es entonces muy simple:

```php
$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'El valor debe ser un múltiplo de %d',
		8,
	);
```

Las reglas de validación personalizadas también se pueden añadir a JavaScript. La condición es que la regla sea un método estático. Su nombre para el validador JavaScript se crea concatenando el nombre de la clase sin barras invertidas `\`, un guion bajo `_` y el nombre del método. Por ejemplo, `App\MyValidators::validateDivisibility` se escribe como `AppMyValidators_validateDivisibility` y se añade al objeto `Nette.validators`:

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


Evento onValidate
=================

Después de enviar el formulario, se realiza la validación, donde se comprueban las reglas individuales añadidas mediante `addRule()` y luego se dispara el [evento |nette:glossary#Eventos] `onValidate`. Su handler se puede usar para validación adicional, típicamente para verificar la combinación correcta de valores en múltiples elementos del formulario.

Si se detecta un error, lo pasamos al formulario con el método `addError()`. Se puede llamar en un elemento específico o directamente en el formulario.

```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('Esta combinación no es posible.');
	}
}
```


Errores durante el procesamiento
================================

En muchos casos, nos enteramos del error solo cuando procesamos un formulario válido, por ejemplo, al escribir un nuevo elemento en la base de datos y encontrar una duplicidad de claves. En tal caso, nuevamente pasamos el error al formulario con el método `addError()`. Se puede llamar en un elemento específico o directamente en el formulario:

```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('Contraseña inválida.');
	}
}
```

Si es posible, recomendamos adjuntar el error directamente al elemento del formulario, ya que se mostrará junto a él al usar el renderizador predeterminado.

```php
$form['date']->addError('Lo sentimos, pero esta fecha ya está ocupada.');
```

Puede llamar a `addError()` repetidamente para pasar múltiples mensajes de error al formulario o elemento. Los obtiene usando `getErrors()`.

Tenga en cuenta que `$form->getErrors()` devuelve un resumen de todos los mensajes de error, incluidos los que se pasaron directamente a elementos individuales, no solo directamente al formulario. Los mensajes de error pasados solo al formulario se obtienen a través de `$form->getOwnErrors()`.


Modificación de la entrada
==========================

Usando el método `addFilter()`, podemos modificar el valor introducido por el usuario. En este ejemplo, toleraremos y eliminaremos los espacios en el código postal:

```php
$form->addText('zip', 'Código Postal:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // eliminamos los espacios del código postal
	})
	->addRule($form::Pattern, 'El código postal no tiene el formato de cinco dígitos', '\d{5}');
```

El filtro se integra entre las reglas y condiciones de validación, por lo que el orden de los métodos importa, es decir, el filtro y la regla se llaman en el mismo orden que los métodos `addFilter()` y `addRule()`.


Validación JavaScript
=====================

El lenguaje para formular condiciones y reglas es muy potente. Todas las construcciones funcionan tanto en el lado del servidor como en el lado de JavaScript. Se transmiten en atributos HTML `data-nette-rules` como JSON. La validación en sí la realiza un script que captura el evento `submit` del formulario, recorre los elementos individuales y realiza la validación correspondiente.

Ese script es `netteForms.js` y está disponible desde múltiples fuentes posibles:

Puede insertar el script directamente en la página HTML desde un CDN:

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

O copiarlo localmente a la carpeta pública del proyecto (p. ej., desde `vendor/nette/forms/src/assets/netteForms.min.js`):

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

O instalarlo a través de [npm|https://www.npmjs.com/package/nette-forms]:

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

Y luego cargarlo y ejecutarlo:

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

Alternativamente, puede cargarlo directamente desde la carpeta `vendor`:

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


JavaScript dinámico
===================

¿Quiere mostrar los campos para introducir la dirección solo si el usuario elige enviar la mercancía por correo? No hay problema. La clave es el par de métodos `addCondition()` & `toggle()`:

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

Este código dice que cuando se cumple la condición, es decir, cuando la casilla de verificación está marcada, el elemento HTML `#address-container` será visible. Y viceversa. Así, colocamos los elementos del formulario con la dirección del destinatario en un contenedor con este ID y al hacer clic en la casilla de verificación se ocultarán o mostrarán. Esto lo asegura el script `netteForms.js`.

Como argumento del método `toggle()`, se puede pasar cualquier selector. Por razones históricas, una cadena alfanumérica sin otros caracteres especiales se entiende como el ID del elemento, es decir, igual que si le precediera el carácter `#`. El segundo parámetro opcional permite invertir el comportamiento, es decir, si usáramos `toggle('#address-container', false)`, el elemento se mostraría solo si la casilla de verificación no estuviera marcada.

La implementación predeterminada en JavaScript cambia la propiedad `hidden` de los elementos. Sin embargo, podemos cambiar fácilmente el comportamiento, por ejemplo, añadir una animación. Simplemente sobrescriba el método `Nette.toggle` en JavaScript con su propia solución:

```js
Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// ocultamos o mostramos 'el' según el valor de 'visible'
	});
};
```


Desactivación de la validación
==============================

A veces puede ser útil desactivar la validación. Si al presionar un botón de envío no se debe realizar la validación (adecuado para botones *Cancelar* o *Vista previa*), la desactivamos con el método `$submit->setValidationScope([])`. Si debe realizar solo una validación parcial, podemos especificar qué campos o contenedores de formulario se deben validar.

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

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

$form->addSubmit('send1'); // Valida todo el formulario
$form->addSubmit('send2')
	->setValidationScope([]); // No valida nada
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Valida solo el elemento name
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Valida solo el elemento age
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Valida el contenedor details
```

`setValidationScope` no afecta al [#evento onValidate] del formulario, que siempre será llamado. El evento `onValidate` de un contenedor solo se activará si ese contenedor está marcado para validación parcial.

Validación de formularios

Elementos obligatorios

Marcamos los elementos obligatorios con el método setRequired(), cuyo argumento es el texto del mensaje de error que se mostrará si el usuario no rellena el elemento. Si no se proporciona el argumento, se utilizará el mensaje de error predeterminado.

$form->addText('name', 'Nombre:')
	->setRequired('Por favor, introduzca su nombre');

Reglas

Añadimos reglas de validación a los elementos con el método addRule(). El primer parámetro es la regla, el segundo es el texto del mensaje de error y el tercero es el argumento de la regla de validación.

$form->addPassword('password', 'Contraseña:')
	->addRule($form::MinLength, 'La contraseña debe tener al menos %d caracteres', 8);

Las reglas de validación solo se verifican si el usuario ha rellenado el elemento.

Nette viene con una serie de reglas predefinidas, cuyos nombres son constantes de la clase Nette\Forms\Form. Podemos usar estas reglas para todos los elementos:

constante descripción tipo de argumento
Required elemento obligatorio, alias para setRequired()
Filled elemento obligatorio, alias para setRequired()
Blank el elemento no debe ser rellenado
Equal el valor es igual al parámetro mixed
NotEqual el valor no es igual al parámetro mixed
IsIn el valor es igual a uno de los elementos del array array
IsNotIn el valor no es igual a ninguno de los elementos del array array
Valid ¿está el elemento rellenado correctamente? (para condiciones)

Entradas de texto

Para los elementos addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat(), también se pueden usar algunas de las siguientes reglas:

MinLength longitud mínima del texto int
MaxLength longitud máxima del texto int
Length longitud en un rango o longitud exacta par [int, int] o int
Email dirección de correo electrónico válida
URL URL absoluta
Pattern coincide con la expresión regular string
PatternInsensitive como Pattern, pero insensible a mayúsculas/minúsculas string
Integer valor entero
Numeric alias para Integer
Float número
Min valor mínimo del elemento numérico int|float
Max valor máximo del elemento numérico int|float
Range valor en el rango par [int|float, int|float]

Las reglas de validación Integer, Numeric y Float convierten directamente el valor a entero o flotante, respectivamente. Además, la regla URL también acepta una dirección sin esquema (p. ej., nette.org) y añade el esquema (https://nette.org). La expresión en Pattern y PatternIcase debe aplicarse a todo el valor, es decir, como si estuviera envuelta en los caracteres ^ y $.

Número de elementos

Para los elementos addMultiUpload(), addCheckboxList(), addMultiSelect(), también se pueden usar las siguientes reglas para limitar el número de elementos seleccionados o archivos cargados:

MinLength número mínimo int
MaxLength número máximo int
Length número en un rango o número exacto par [int, int] o int

Carga de archivos

Para los elementos addUpload(), addMultiUpload(), también se pueden usar las siguientes reglas:

MaxFileSize tamaño máximo del archivo en bytes int
MimeType Tipo MIME, se permiten comodines ('video/*') string|string[]
Image imagen JPEG, PNG, GIF, WebP, AVIF
Pattern el nombre del archivo coincide con la expresión regular string
PatternInsensitive como Pattern, pero insensible a mayúsculas/minúsculas string

MimeType e Image requieren la extensión PHP fileinfo. Detectan si el archivo o imagen es del tipo requerido basándose en su firma y no verifican la integridad de todo el archivo. Si una imagen está dañada se puede detectar, por ejemplo, intentando cargarla.

Mensajes de error

Todas las reglas predefinidas, excepto Pattern y PatternInsensitive, tienen un mensaje de error predeterminado, por lo que se puede omitir. Sin embargo, al proporcionar y formular todos los mensajes a medida, hará que el formulario sea más fácil de usar.

Puede cambiar los mensajes predeterminados en la configuración, editando los textos en el array Nette\Forms\Validator::$messages o usando un traductor.

En el texto de los mensajes de error se pueden usar estas cadenas de marcador de posición:

%d reemplaza secuencialmente con los argumentos de la regla
%n$d reemplaza con el n-ésimo argumento de la regla
%label reemplaza con la etiqueta del elemento (sin dos puntos)
%name reemplaza con el nombre del elemento (p. ej. name)
%value reemplaza con el valor introducido por el usuario
$form->addText('name', 'Nombre:')
	->setRequired('Por favor, rellene %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'al menos %d y como máximo %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'como máximo %2$d y al menos %1$d', [5, 10]);

Condiciones

Además de las reglas, también se pueden añadir condiciones. Se escriben de forma similar a las reglas, solo que en lugar de addRule() usamos el método addCondition() y, por supuesto, no proporcionamos ningún mensaje de error (la condición solo pregunta):

$form->addPassword('password', 'Contraseña:')
	// si la contraseña no tiene más de 8 caracteres
	->addCondition($form::MaxLength, 8)
		// entonces debe contener un dígito
		->addRule($form::Pattern, 'Debe contener un dígito', '.*[0-9].*');

La condición también se puede vincular a un elemento diferente al actual usando addConditionOn(). Como primer parámetro, proporcionamos una referencia al elemento. En este ejemplo, el correo electrónico será obligatorio solo si se marca la casilla de verificación (su valor será true):

$form->addCheckbox('newsletters', 'Enviarme boletines');

$form->addEmail('email', 'E-mail:')
	// si la casilla de verificación está marcada
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// entonces requiere el correo electrónico
		->setRequired('Introduzca una dirección de correo electrónico');

Se pueden crear estructuras complejas a partir de condiciones usando elseCondition() y endCondition():

$form->addText(/* ... */)
	->addCondition(/* ... */) // si se cumple la primera condición
		->addConditionOn(/* ... */) // y la segunda condición en otro elemento
			->addRule(/* ... */) // requiere esta regla
		->elseCondition() // si la segunda condición no se cumple
			->addRule(/* ... */) // requiere estas reglas
			->addRule(/* ... */)
		->endCondition() // volvemos a la primera condición
		->addRule(/* ... */);

En Nette, es muy fácil reaccionar al cumplimiento o incumplimiento de una condición también en el lado de JavaScript usando el método toggle(), consulte JavaScript dinámico.

Referencia a otro elemento

También se puede pasar otro elemento del formulario como argumento de una regla o condición. La regla entonces usará el valor introducido posteriormente por el usuario en el navegador. De esta manera, se puede validar dinámicamente, por ejemplo, que el elemento password contenga la misma cadena que el elemento password_confirm:

$form->addPassword('password', 'Contraseña');
$form->addPassword('password_confirm', 'Confirmar contraseña')
    ->addRule($form::Equal, 'Las contraseñas introducidas no coinciden', $form['password']);

Reglas y condiciones personalizadas

A veces nos encontramos en una situación en la que las reglas de validación incorporadas en Nette no son suficientes y necesitamos validar los datos del usuario a nuestra manera. ¡En Nette esto es muy simple!

Se puede pasar cualquier callback como primer parámetro a los métodos addRule() o addCondition(). Este recibe el propio elemento como primer parámetro y devuelve un valor booleano que indica si la validación se realizó correctamente. Al añadir una regla con addRule(), también es posible especificar argumentos adicionales, que luego se pasan como segundo parámetro.

Así podemos crear nuestro propio conjunto de validadores como una clase con métodos estáticos:

class MyValidators
{
	// comprueba si el valor es divisible por el argumento
	public static function validateDivisibility(Nette\Forms\Control $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(Nette\Forms\Control $input, $domain)
	{
		// otros validadores
	}
}

El uso es entonces muy simple:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'El valor debe ser un múltiplo de %d',
		8,
	);

Las reglas de validación personalizadas también se pueden añadir a JavaScript. La condición es que la regla sea un método estático. Su nombre para el validador JavaScript se crea concatenando el nombre de la clase sin barras invertidas \, un guion bajo _ y el nombre del método. Por ejemplo, App\MyValidators::validateDivisibility se escribe como AppMyValidators_validateDivisibility y se añade al objeto Nette.validators:

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

Evento onValidate

Después de enviar el formulario, se realiza la validación, donde se comprueban las reglas individuales añadidas mediante addRule() y luego se dispara el evento onValidate. Su handler se puede usar para validación adicional, típicamente para verificar la combinación correcta de valores en múltiples elementos del formulario.

Si se detecta un error, lo pasamos al formulario con el método addError(). Se puede llamar en un elemento específico o directamente en el formulario.

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('Esta combinación no es posible.');
	}
}

Errores durante el procesamiento

En muchos casos, nos enteramos del error solo cuando procesamos un formulario válido, por ejemplo, al escribir un nuevo elemento en la base de datos y encontrar una duplicidad de claves. En tal caso, nuevamente pasamos el error al formulario con el método addError(). Se puede llamar en un elemento específico o directamente en el formulario:

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('Contraseña inválida.');
	}
}

Si es posible, recomendamos adjuntar el error directamente al elemento del formulario, ya que se mostrará junto a él al usar el renderizador predeterminado.

$form['date']->addError('Lo sentimos, pero esta fecha ya está ocupada.');

Puede llamar a addError() repetidamente para pasar múltiples mensajes de error al formulario o elemento. Los obtiene usando getErrors().

Tenga en cuenta que $form->getErrors() devuelve un resumen de todos los mensajes de error, incluidos los que se pasaron directamente a elementos individuales, no solo directamente al formulario. Los mensajes de error pasados solo al formulario se obtienen a través de $form->getOwnErrors().

Modificación de la entrada

Usando el método addFilter(), podemos modificar el valor introducido por el usuario. En este ejemplo, toleraremos y eliminaremos los espacios en el código postal:

$form->addText('zip', 'Código Postal:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // eliminamos los espacios del código postal
	})
	->addRule($form::Pattern, 'El código postal no tiene el formato de cinco dígitos', '\d{5}');

El filtro se integra entre las reglas y condiciones de validación, por lo que el orden de los métodos importa, es decir, el filtro y la regla se llaman en el mismo orden que los métodos addFilter() y addRule().

Validación JavaScript

El lenguaje para formular condiciones y reglas es muy potente. Todas las construcciones funcionan tanto en el lado del servidor como en el lado de JavaScript. Se transmiten en atributos HTML data-nette-rules como JSON. La validación en sí la realiza un script que captura el evento submit del formulario, recorre los elementos individuales y realiza la validación correspondiente.

Ese script es netteForms.js y está disponible desde múltiples fuentes posibles:

Puede insertar el script directamente en la página HTML desde un CDN:

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

O copiarlo localmente a la carpeta pública del proyecto (p. ej., desde vendor/nette/forms/src/assets/netteForms.min.js):

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

O instalarlo a través de npm:

npm install nette-forms

Y luego cargarlo y ejecutarlo:

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

Alternativamente, puede cargarlo directamente desde la carpeta vendor:

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

JavaScript dinámico

¿Quiere mostrar los campos para introducir la dirección solo si el usuario elige enviar la mercancía por correo? No hay problema. La clave es el par de métodos addCondition() & toggle():

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

Este código dice que cuando se cumple la condición, es decir, cuando la casilla de verificación está marcada, el elemento HTML #address-container será visible. Y viceversa. Así, colocamos los elementos del formulario con la dirección del destinatario en un contenedor con este ID y al hacer clic en la casilla de verificación se ocultarán o mostrarán. Esto lo asegura el script netteForms.js.

Como argumento del método toggle(), se puede pasar cualquier selector. Por razones históricas, una cadena alfanumérica sin otros caracteres especiales se entiende como el ID del elemento, es decir, igual que si le precediera el carácter #. El segundo parámetro opcional permite invertir el comportamiento, es decir, si usáramos toggle('#address-container', false), el elemento se mostraría solo si la casilla de verificación no estuviera marcada.

La implementación predeterminada en JavaScript cambia la propiedad hidden de los elementos. Sin embargo, podemos cambiar fácilmente el comportamiento, por ejemplo, añadir una animación. Simplemente sobrescriba el método Nette.toggle en JavaScript con su propia solución:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// ocultamos o mostramos 'el' según el valor de 'visible'
	});
};

Desactivación de la validación

A veces puede ser útil desactivar la validación. Si al presionar un botón de envío no se debe realizar la validación (adecuado para botones CancelarVista previa), la desactivamos con el método $submit->setValidationScope([]). Si debe realizar solo una validación parcial, podemos especificar qué campos o contenedores de formulario se deben validar.

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

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

$form->addSubmit('send1'); // Valida todo el formulario
$form->addSubmit('send2')
	->setValidationScope([]); // No valida nada
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Valida solo el elemento name
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Valida solo el elemento age
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Valida el contenedor details

setValidationScope no afecta al evento onValidate del formulario, que siempre será llamado. El evento onValidate de un contenedor solo se activará si ese contenedor está marcado para validación parcial.