Validace prvků v závislosti na hodnotě jiných prvků

(bojovyletoun)

Jak vyřešit validace formulářového prvku v závislosti na hodnotě jiného prvku?

Vlastně máme dvě možnosti, rozdíl je z pohledu znovupoužitelnosti:

  1. Můžeme použít vlastní validační funkce, které můžeme umístit do samostatného souboru s funkcemi. Výhodou pak je, že tyto funkce můžeme znovuvyužít napříč všemi formuláři.
  2. Využití události (event) onValidate, která je výhodná zvláště v případech, kdy potřebujeme validaci, která je ušitá na míru danému formuláři. Tento způsob popíši jen krátce.

Vlastní validační funkce

Trik je, že použijeme callback jako validační funkci. Callback se pak spustí na vhodném místě s těmito parametry:

  1. vlastní input prvek ($field)
  2. (volitelné) argumenty validační funkce ($args). Může jít o string, cokoli. Pole se právě hodí k předání seznamu vybraných inputů jako v příkladu.

Zde je vysvětlující kód.

//definice validační funkce. Může to být i klasická funkce, ne jen lambda.
$cb = function ($field, $args) {
    return $field->value == $args[0]->value + $args[1]->value;
};

$a = $form->addText('num1', 'N1');
$b = $form->addText('num2', 'N2');

$form->addText('sum', 'Sum')->addRule($cb, 'Sum!', [$a, $b]);

Callback může být složitější, chytřejší, aby uměl pracovat s dynamickým počtem prvků. Pole lze projít pomocí foreach nebo lze využít třeba array_map, array_reduce, array_filter,…

Javascriptová validace

Nevýhodou použití lambda funkce je pak nemožnost vytvoření javascriptové validace. Soubor netteForms.js dokáže zpracovat zápis pro statickou funkci, tudíž pokud zapíšeme pravidlo takto:

$form->addText('sum', 'Sum')->addRule('FormValidators::Sum', 'Součet není správně!', ["field1" => $a, "field2" => $b]);

Tak můžeme vytvořit javascriptovou validaci pomocí skriptu:

<script>
        Nette.validators.FormValidators_Sum = function (elem, args, val) {
            var el1 = document.getElementById("frmform-" + args.field1.control);
            var val1 = Nette.getValue(el1);

            var el2 = document.getElementById("frmform-" + args.field2.control);
            var val2 = Nette.getValue(el2);
            return val == val1 + val2;
        };
</script>

Událost onValidate

Ještě popíšu další způsob validace formuláře. A sice na formulář přidáte event onValidate:

$form = new Form;
//...
$form->onValidate[] = function ($form) {
    if($form['someinput']->value%42)$form->addError(...);
};

Celá ukázka:

(pozměněno z examples)

<?php
/**
 * Nette Forms custom validator example.
 */
require __DIR__ . '/../../Nette/loader.php';

use Nette\Forms\Form,
 Nette\Diagnostics\Debugger;

Debugger::enable();

// Step 1: Define form with validation rules
$form = new Form;
$a = $form->addText('num1', 'N1');
$b = $form->addText('num2', 'N2');

$cb = function ($field, $args) {
    return $field->value == $args[0]->value + $args[1]->value;
};

$cb2 = function ($field, $args) {
    return $field->value == $args[0]->value * $args[1]->value;
};

$form->addText('sum', 'Sum')->addRule($cb, 'Sum!', [$a, $b]);
$form->addText('Mult', 'mul')->addRule($cb2, 'Mul!', [$a, $b]);

$form->addSubmit('submit', 'Send');



// Step 2: Check if form was submitted?
if ($form->isSubmitted()) {

// Step 2c: Check if form is valid
    if ($form->isValid()) {
        echo '<h2>Form was submitted and successfully validated</h2>';

        Debugger::dump($form->values);

        // this is the end, my friend :-)
        exit;
    }
} else {
// not submitted, define default values
    $defaults = [
        'num1' => '5',
        'num2' => '5',
    ];

    $form->setDefaults($defaults);
}



// Step 3: Render form
?>
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">

        <title>Nette Forms custom validator example | Nette Framework</title>

        <style type="text/css">
            .required {
                color: darkred
            }

            fieldset {
                padding: .5em;
                margin: .5em 0;
                background: #E4F1FC;
                border: 1px solid #B2D1EB;
            }

            input.button {
                font-size: 120%;
            }

            th {
                width: 10em;
                text-align: right;
            }
        </style>
        <link rel="stylesheet" type="text/css" media="screen" href="files/style.css" />
    </head>

    <body>
        <h1>Nette Forms custom validator example</h1>

        <?php echo $form ?>
    </body>
</html>

Další info: