Odlišná validace v javascriptu a na serveru Nette\Application\UI\Form – addConditionOn()

m.brecher
Generous Backer | 736
+
0
-

Ahoj,

narazil jsem na tento problém. Mám formulář, kde chci, aby dvojice formulářových prvků byla vyplněná buďto oba dva, nebo žádný. Vytvořil jsem tedy křížově závislá validační pravidla takto:


use Nette;

class TestPresenter extends AdminPresenter
{

    public function createComponentTestForm(): Nette\Application\UI\Form
    {
        $form = new Form;
        $items = ['' => '', 1 => 'PRIVATE_TYPE', 2 => 'PUBLIC_TYPE'];

        $title = $form->addText('title', 'Titulek', 20, 20);

        $form->addSelect('type', 'Typ', $items)
            ->addConditionOn($form['title'], $form::FILLED)		// první křížové pravidlo
            ->setRequired('Musíte vyplnit také typ');

        $title->addConditionOn($form['type'], $form::FILLED)	// druhé křížové pravidlo
            ->setRequired('Musíte vyplnit také titulek');

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

        $form->onSuccess[] = function (){ /* do something */ };

        return $form;
    }
}

Formulář vykresluji test.latte:

{form testForm}
   {var $indexes = ['title', 'type']}
   {foreach $indexes as $index}
         {var $input = $form[$index]}
       {label $input}
       {input $input}
       {inputError $input}
       <br>
   {/foreach}
   {input $form['submit']}
{/form}

Javascriptová validace Nette funguje jak má – když jsou oba prvky prázdné, chybu nehlásí, když jeden z prvků je vyplněný a druhý ne hlásí chybu.

Serverová validace se chová odlišně – když není vyplněn ani jeden prvek hlásí „Musíte vyplnit také titulek“, což není správně, protože required by měl být aplikován pouze v případě, že druhý prvek má vybranou hodnotu.

Zkusil jsem i nastavit defaultní hodnotu selectu $typ null:


        $form['type']->setDefaultValue(null);

Nepomohlo to.

Poraďte prosím, zda tam nemám chybu, kterou nevidím, nebo je to „vlastnost“ Frameworku.

Určitě toto chování znemožňuje normální funkci formuláře, protože právě potřebuji, aby bylo možné dvojici nevyplnit.

Díky předem za odpovědi.

Polki
Člen | 553
+
0
-

Nejde o vlastnost FW ty máš chybu v kódu.

Pokud zadáš selectu hodnoty tak jak to máš: ['' => '', 1 => 'PRIVATE_TYPE', 2 => 'PUBLIC_TYPE']

Tak se každá z těch hodnot bere jako že jsi prvek vyplnil, což je očekávané chování, jelikož byl zvolen jeden z výčtu…

Pokud chceš aby se prvek choval jako nevyplněný, tak od toho v případě selectu existuje funkce setPrompt, která podle dokumentace nastavuje dodatečný option, který když je vybrán, tak se select chová jako nevyplněný, takže správně má být tvůj kód takto:

public function createComponentTestForm(): Form
{
	$form = new Form();
	$items = [1 => 'PRIVATE_TYPE', 2 => 'PUBLIC_TYPE'];

	$title = $form->addText('title', 'Titulek', 20, 20);

	$form->addSelect('type', 'Typ', $items)
		->setPrompt('')
		->addConditionOn($form['title'], $form::FILLED)		// první křížové pravidlo
		->setRequired('Musíte vyplnit také typ');

	$title->addConditionOn($form['type'], $form::FILLED)	// druhé křížové pravidlo
	->setRequired('Musíte vyplnit také titulek');

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

	$form->onSuccess[] = function (){ /* do something */ };

	return $form;
}
m.brecher
Generous Backer | 736
+
0
-

@Polki Ahoj, díky za vysvětlení, setPrompt('') jsem dříve používal, ale pro vložení nevyplněné položky selectu mě přišlo čitelnější vložit null položku do položek selectu takto:


$items = [null => ''] + $arrayItems
$items = ['' => ''] + $arrayItems  // nebo takhle

$select->setRequired(‚…‘) zachází s položkou [null ⇒ ''] jako s NEVYBRANOU položku selectu a to jak v JS validaci, tak i v serverové validaci, funguje úplně stejně jako ->setPrompt(). Používám to už půl roku bez problémů.

Dnes jsem poprvé dělal validaci pomocí $form::FILLED, což by dle dokumentace měl být ekvivalent ->setRequired(), ale není. Odlišnost je v tom, že JS validace tam funguje stejně jako ->setRequired(), tj. položka indexovaná '' je chápána jako nevybraná (prompt), ale v serverové validaci se chování liší – je to jak popisuješ, položku indexovanou '' bere jako vybranou hodnotu.

Takže je ve FW nejednotnost ve validaci položky indexované null nebo ''.

Vypadá to tak, že byl záměr aby to takto fungovalo – ze 4 variant to takto funguje ve 3 variantách a pouze v jedné variantě nikoliv.

Mě osobně by se líbilo, kdyby šlo psát $items = [null ⇒ ‚vyberte položku‘] + $array, protože to je takové moderní a self-documented. Stačilo by fixnout tu drobnou odlišnost v serverové validaci $form::FILLED.

Jinak dík za komentář.

Editoval m.brecher (22. 2. 2022 17:36)

m.brecher
Generous Backer | 736
+
0
-

@Polki Přikládám ještě ukázku kódu:


class TestPresenter extends AdminPresenter
{

    public function createComponentTestForm(): Form
    {
        $form = new Form;
//        $items = [null => '', 1 => 'PRIVATE_TYPE', 2 => 'PUBLIC_TYPE'];
        $items = [1 => 'PRIVATE_TYPE', 2 => 'PUBLIC_TYPE'];    // tohle se setPrompt() funguje !!

        $title = $form->addText('title', 'Titulek', 20, 20)->setHtmlAttribute('placeholder','vyplňte něco!');

        $form->addSelect('type', 'Typ', $items)
            ->setPrompt('zvolte typ')
            ->addConditionOn($form['title'], $form::FILLED)
            ->setRequired('Musíte vyplnit také typ');

        $title->addConditionOn($form['type'], $form::FILLED)
            ->setRequired('Musíte vyplnit také titulek');

        $form->addSelect('direction', 'Směr', ['' => 'vyber směr!'] + ['FORWARD', 'BACKWARD'])  // se setRequired() funguje vždy !!
            ->setRequired('Zvolte směr!');

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

        $form['type']->setDefaultValue(null);

        $form->onSuccess[] = function (){ /* do something */ };

        return $form;
    }
}