addHidden ve formuláři načte hodnotu jen jednou

Allconius
Člen | 313
+
0
-

Ahoj, mám tento formulář:

<?php

    protected function createComponentTestForm()
    {

    $form = new Form;
    $result = $this->dbManager->zobrazTest(1); //pocet RAND otazek

    foreach ($result as $row) {


.........................

        $cislootazky = (string)$row->id_otazky;

    }

    $form->addHidden('otazka', $cislootazky);
    $form->addHidden('hledej','1');
    $form->addSubmit('submit', 'Další');
    $form->onSuccess[] = [$this, 'testFormSucceeded'];

    return $form;
    }


    public function testFormSucceeded(Form $form, $values)
    {

        echo $values['otazka'];
    }

?>

Generuji s ním testové otázky a problém je v tom, že se to

<?php
$form->addHidden('otazka', $cislootazky);
?>

naplní jen jednou při prvním odeslání, tzn. ve zdrojáku mám např::

<?php
<input type="hidden" name="otazka" value="99">
<input type="hidden" name="hledej" value="1">
<input type="hidden" name="_do" value="testForm-submit">
?>

a je tam pořád hodnota „99“ i když čísla otázek už jsou úplně jiná, proč tam pořád drží tu hodnotu načtenou poprvé ? Nějaký session … ? Mám pak nesmyslnej zdroják např.:

<?php
<form action="/test/" method="post" class="form-horizontal" id="frm-testForm">

<fieldset>
<legend>1. Cyklista odbočující vpravo:</legend>
<img src="../obrazky/262.png" alt="Cyklista odbočující vpravo:" />

<div class="form-group">
<div class="col-sm-3 control-label">
<label></label>
</div>

<div class="col-sm-9">
<label>
<input type="radio" name="otazka262" value="3"> dává přednost žlutému vozidlu, přijíždějícímu zleva
</label><br>
<label>
<input type="radio" name="otazka262" value="2"> má přednost v jízdě před protijedoucím modrým vozidlem
</label><br>
<label>
<input type="radio" name="otazka262" value="1"> musí dát přednost v jízdě protijedoucímu modrému vozidlu
</label>
</div>
</div>

<div class="form-group">
<div class="col-sm-3 control-label"></div>
<div class="col-sm-9"><input type="submit" name="_submit" value="Další" class="button"></div>
</div>

</fieldset>

<input type="hidden" name="otazka" value="99">
<input type="hidden" name="hledej" value="1">
<input type="hidden" name="_do" value="testForm-submit">
<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
</form>
?>

kdy načítám otázku 262, ale v hidden-otazka mám 99, zkoušel jsem mazat temp a nic, načte ten hidden-otazka jen jednou a pak to „drží“

Editoval Allconius (8. 10. 2019 9:50)

Ondřej Kubíček
Člen | 494
+
+1
-

a v proměnné $cislootazky je co? tam je 262 nebo 99? nevím jak ten foreach vypadá celý, ale tipuju že 99 je vždy poslední z toho $result a proto si ho vždy nastavíš do té proměnné

Allconius
Člen | 313
+
0
-

Ondřej Kubíček napsal(a):

a v proměnné $cislootazky je co? tam je 262 nebo 99? nevím jak ten foreach vypadá celý, ale tipuju že 99 je vždy poslední z toho $result a proto si ho vždy nastavíš do té proměnné

Ahoj, je to takto:

<?php
    $form = new Form;
    $result = $this->dbManager->zobrazTest(1); //pocet RAND otazek
    $cislo = 1;
    $numbers = range(1, 3);

    foreach ($result as $row) {

        if (($row->ref('besip_otazky', 'id_otazky')->obrazek)<>'') {

        $form->addGroup($cislo.'. '.$row->ref('besip_otazky', 'id_otazky')->otazka)
           ->setOption('description',Html::el()->setHtml('<img src="../obrazky/'.$row->ref('besip_otazky', 'id_otazky')->obrazek.'.png" alt="'.$row->ref('besip_otazky', 'id_otazky')->otazka.'" />'));

        }else{

        $form->addGroup($cislo.'. '.$row->ref('besip_otazky', 'id_otazky')->otazka);

        }

//namixuj to nahodne
    shuffle($numbers);
        foreach ($numbers as $number) {

        $m = 'm'.$number;
        $odpovedi[$number] = ' '.$row->ref('besip_otazky', 'id_otazky')->$m.'';

        }

        // pro vypsání možností do 1 řádku
        $form->addRadioList('otazka'.$row->ref('besip_otazky', 'id_otazky')->id_otazky.'', '', $odpovedi);
        //->getSeparatorPrototype()->setName(null); //hodnoty v 1 radku
        $cislo++;

        $cislootazky = (string)$row->id_otazky;
        //$cislootazky = '0';

    }

    $form->addHidden('otazka', $cislootazky);
    $form->addHidden('hledej','1');
    $form->addSubmit('submit', 'Další');

?>

no právě nechápu odkud to bere, protože to číslo 1 zde:

<?php
$result = $this->dbManager->zobrazTest(1);
?>

znamená, že vybírám jen jednu otázku, a první načtení stránky se provede dobře:

<?php
<form action="/test/" method="post" class="form-horizontal" id="frm-testForm">

<fieldset>
<legend>1. Cyklista projede křižovatkou jako:</legend>
<img src="../obrazky/310.png" alt="Cyklista projede křižovatkou jako:" />

<div class="form-group">
	<div class="col-sm-3 control-label"><label></label></div>

	<div class="col-sm-9"><label><input type="radio" name="otazka310" value="1"> první</label><br><label><input type="radio" name="otazka310" value="2"> třetí současně s modrým vozidlem</label><br><label><input type="radio" name="otazka310" value="3"> druhý</label></div>
</div>

<div class="form-group">
	<div class="col-sm-3 control-label"></div>

	<div class="col-sm-9"><input type="submit" name="_submit" value="Další" class="button"></div>
</div>

</fieldset>

<input type="hidden" name="otazka" value="310"><input type="hidden" name="hledej" value="1"><input type="hidden" name="_do" value="testForm-submit"><!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
</form>
?>

je tam všude 310, ale když dám odeslat tak tam mám:

<?php
<form action="/test/" method="post" class="form-horizontal" id="frm-testForm">

<fieldset>
<legend>1. Pokud se závory  sklápějí nebo zdvihají,  smí se vjíždět na železniční přejezd</legend>
<img src="../obrazky/117.png" alt="Pokud se závory  sklápějí nebo zdvihají,  smí se vjíždět na železniční přejezd" />

<div class="form-group">
	<div class="col-sm-3 control-label"><label></label></div>

	<div class="col-sm-9"><label><input type="radio" name="otazka117" value="1"> ano</label><br><label><input type="radio" name="otazka117" value="2"> ne</label><br><label><input type="radio" name="otazka117" value="3"> nevím</label></div>
</div>

<div class="form-group">
	<div class="col-sm-3 control-label"></div>

	<div class="col-sm-9"><input type="submit" name="_submit" value="Další" class="button"></div>
</div>

</fieldset>

<input type="hidden" name="otazka" value="262"><input type="hidden" name="hledej" value="1"><input type="hidden" name="_do" value="testForm-submit"><!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
</form>
?>

tak tam mám najednou 262 místo 117, a další odeslání:

<?php
<form action="/test/" method="post" class="form-horizontal" id="frm-testForm">

<fieldset>
<legend>1. Dodatková tabulka vyznačuje</legend>
<img src="../obrazky/189.png" alt="Dodatková tabulka vyznačuje" />

<div class="form-group">
	<div class="col-sm-3 control-label"><label></label></div>

	<div class="col-sm-9"><label><input type="radio" name="otazka189" value="3"> zvýšení počtu jízdních pruhů pro cyklisty</label><br><label><input type="radio" name="otazka189" value="1"> možný směr jízdy cyklistů</label><br><label><input type="radio" name="otazka189" value="2"> křižovatku</label></div>
</div>

<div class="form-group">
	<div class="col-sm-3 control-label"></div>

	<div class="col-sm-9"><input type="submit" name="_submit" value="Další" class="button"></div>
</div>

</fieldset>

<input type="hidden" name="otazka" value="262"><input type="hidden" name="hledej" value="1"><input type="hidden" name="_do" value="testForm-submit"><!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
</form>
?>

zase 262 místo 189 a to 262 už tam pak zůstane dokud nezavřu prohlížeč :-) Asi je to jen nějaká blbost, ale nemůžu na to přijít. Každopádně to číslo co tam zůstává je pokaždé jiný a není stejné jako prvně načtená otázka. Třeba teď jsem to zkoušel a první otázka měla číslo 19, po odkliknutí se načetla otázka 65, ale to číslo v hidden-otazka se zafixovalo na čísle 12 :-)

Ondřej Kubíček
Člen | 494
+
0
-

bwt když víš že chceš dostat jen jeden záznam použij fetch(), ne fetchAll() (za předpokladu že používáš nette database, nevím co zobrazTest dělá a co je dbManager), pak je uplně zbytečné používat foreach pro $result

Allconius
Člen | 313
+
0
-

Ondřej Kubíček napsal(a):

a v proměnné $cislootazky je co? tam je 262 nebo 99? nevím jak ten foreach vypadá celý, ale tipuju že 99 je vždy poslední z toho $result a proto si ho vždy nastavíš do té proměnné

Nemůže nějak blbnout ten (string) ? Protože když jsem přidal:

<?php
        echo 'id_otazky='.$row->id_otazky.'<br />';
        $cislootazky = (string)$row->id_otazky;
?>

tak to echo ukazuje správné číslo.

Šaman
Člen | 2634
+
0
-

Nechci se teď prokousávat těmi bootstrap šablonami, ale obecně se formuláře plní v action metodě. Zvlášť, když chceš náhodné hodnoty. Protože formulář se vždy vytvoří nejméně dvakrát – poprvé když se vykreslí a podruhé jako příjemce hodnot. Ty vytvoříš pokaždé jiný formulář a Nette si nějak hlídá, aby hodnota předaná při předání POSTem byla jedna z možných hodnot toho formuláře.

Vytvoř si formulář jen jako políčka bez hodnot a pak ho předvyplň v action metodě (v render je pozdě!). Ušetříš si různé WTF momenty, možná se to vyřeší.

Allconius
Člen | 313
+
0
-

Ondřej Kubíček napsal(a):

bwt když víš že chceš dostat jen jeden záznam použij fetch(), ne fetchAll() (za předpokladu že používáš nette database, nevím co zobrazTest dělá a co je dbManager), pak je uplně zbytečné používat foreach pro $result

no teď jsem zjistil že je to správně i po foreach, když vyechuju:

<?php
    echo 'cislootazky='.$cislootazky.'<br />';

    $form->addHidden('otazka', $cislootazky);
    $form->addHidden('hledej','1');
    $form->addSubmit('submit', 'Další');
?>

tak dostanu přes:

<?php
    public function testFormSucceeded(Form $form, $values)
    {
        echo print_r($values);
    }
?>

toto:

<?php
cislootazky=284
Nette\Utils\ArrayHash Object ( [otazka284] => [otazka] => 83 [hledej] => 1 ) 1
?>

nějak to „zprasí“ až ten addHidden

Šaman
Člen | 2634
+
0
-

A co to dělá, když znova načteš stránku „nanovo“? Bez zavření prohlížeče, ale taky bez toho, že by ses na ten formulář dostal odesláním předchozího formuláře.

Allconius
Člen | 313
+
0
-

Šaman napsal(a):

A co to dělá, když znova načteš stránku „nanovo“? Bez zavření prohlížeče, ale taky bez toho, že by ses na ten formulář dostal odesláním předchozího formuláře.

Ahoj, máš pravdu, když jen reloaduju stránku F5 tak to funguje správně, problém je když dám submit, byl tu včera kamarád a radil mi to samé co ty, že to nemám rvat do té komponenty ale použít:

<?php
    public function renderDefault()
    {
        $this['testForm']->setDefaults(array('otazka'=>"187"));
    }
?>

nebo asi tedy už do action:

<?php
    public function actionDefault()
    {
        $this['testForm']->setDefaults(array('otazka'=>"187"));
    }
?>

tak to zkusím předělat je pravda že s tou modifikací komponenty jsem měl vždycky problém dřív jsem zkoušel např. modifikace přes proměnnou:

<?php

if ($a==1){
$form->addHidden('otazka', $cislootazky);
}else{
$form->addText('jouda', 10, 10);
}
    $form->addSubmit('submit', 'Další');

?>

ale to jsem rychle opustil :-)))

CZechBoY
Člen | 3608
+
0
-

A je potreba vubec mit ten hidden field kdyz ho znas i bez toho formulare?

Allconius
Člen | 313
+
0
-

CZechBoY napsal(a):

A je potreba vubec mit ten hidden field kdyz ho znas i bez toho formulare?

no to je otázka jak ho do toho action dostanu … :-)

Šaman
Člen | 2634
+
0
-

V tom případě je chyba asi jinde. V (ne)přesměrování po odeslání. Ty vytvoříš formulář i s nějakými hodnotami. Napoprvé dobrý, ale pak ho odešleš. Odeslání je nový request, takže se znovu vytvoří formulář s novými hodnotami… ale hned přijme data z postu, která mu to id přepíšou tou odeslanou. Teď to musíš zpracovat a přesměrovat na nový čerstvý formulář a to asi neděláš. Takže máš nastavené id otázky tak, jak přišlo postem a po odeslání tedy odesíláš minulou hodnotu. Žádné session, předáváš si to tím polem ve formuláři.

Nezůstává ti v adresním řádku nějaké &do=testFormSubmitted nebo tak nějak? V takovém případě ti v onSuccess chybí redirect, který (pokud moc dobře nevíš proč ho nedělat) by se měl dělat vždy. (Koukám že v prvním příspěvku v kódu opravdu chybí, ale myslel jsem že to echo je jen zoufalé testování. Jestli jsi ho tam neměl ani předtím, tak je to tím.)


A trochu nesouvisející druhá věc – proč neplnit formulář v render metodě. Jakmile si začneš hrát s AJAXem (třeba kvůli závislým inputům, které budeš řešit pomocí snippetů) tak budeš používat handle metody. A z životního cyklu presenteru je vidět, že handler proběhne dřív, než render. Jinak řečeno pracuješ se stále nenastaveným formulářem a do něho se pak hodnoty nasypou (a cokoliv předchozího přepíšou) až při vykreslení (render). Řešením je nastavovat to v action, která proběhne jako první a render metody používat ideálně striktně jen pro práci s šablonou (tedy v render by měly být jen řádky $this->template… a to, co s nimi bezprostředně souvisí).

Editoval Šaman (9. 10. 2019 16:13)

m.brecher
Generous Backer | 732
+
0
-

Včera jsem také narazil na problém, že input hidden ve formuláři Nette držel pořád stejnou hodnotu, kterou jsem inicializoval na začátku a sice metodou $hidden->setDefaultValues(), zkoušel jsem i starší deprecated metodu $hidden->setValue(), hodnotu načetl na začátku a pak ji držel až do konce a ignoroval další zápisy.

Podrobil jsem to pečlivému testování a dospěl k názoru, že se nejedná o chybu v mém kódu, ani o chybu v Nette formulářích, ale je to prostě VLASTNOST Nette formulářů – prvku hiden se dá nastavit pouze default value na začátku a pak už value může měnit pouze uživatel v prohlížeči. Nette je framework, který jako všechny frameworky přináší skvělé pohodlí, ale člověka omezuje a některé chování je logické, ale neočekávané. Kdyby alespoň Nette vyhodilo nějakou Notice o ignorování metody, člověk na tom stráví hodiny, než zjistí čím to je.

m.brecher
Generous Backer | 732
+
0
-

@Šaman S Nette začínám a protože školení je díky lockdownu v nedohlednu tak plním formulář daty na konci tovární metody createComponent<Form> – přišlo mě logičtější, když se formulář naplní sám, než aby ho musel plnit presenter, ten by měl dělat řízení. Když se formulář naplní v tovární metodě tak je pak 100% jistota, že defaultní data nepřepíše AJAX. Navíc některé formuláře mají strukturu závislou na datech – např. počet položek faktury – v tovární metodě se pak tak jako tak musí požádat presenter o dodání modelové třídy která formuláři dodá data, totéž platí při plnění položek select z databáze. Jsou ještě nějaké jiné důvody, které jako začátečník nevidím pro to, aby se data do formuláře nasypaly v action metodě presenteru?

Editoval m.brecher (12. 3. 2021 1:10)

Šaman
Člen | 2634
+
+1
-

Musím se podívat, jestli se něco nezměnilo. Takhle od boku vidím jednu věc:

$form->setDefaults();
$input->setDefaultValue();

takže ono $hidden->setDefaultValues() by nemělo fungovat v žádné verzi.


Formulář se plní v action metodě proto, že to je první místo, kde už znáš konkrétní ID editované položky. V továrně ještě nevíš, jaký záznam budeš editovat. To zjistíš až v

public function actionEdit(int $id): void
{
	$user = $this->userRepository->get($id);
	$this['userForm']->setDefaults($user);
	$this['userForm']['ok']->caption = 'Uložit změny';
}

Editoval Šaman (12. 3. 2021 13:21)

lookass
Člen | 54
+
0
-

@mbrecher Chtěl bych se zeptat, jaká data potřebujete do hidden inputu dát?
Upřímně řečeno, od té doby, co používám Nette jsem asi nikdy hidden input nutně nepotřeboval (už vůbec ne na data typu ID záznamu, který chci editovat).

m.brecher
Generous Backer | 732
+
0
-

lookass napsal(a):

@mbrecher Chtěl bych se zeptat, jaká data potřebujete do hidden inputu dát?
Upřímně řečeno, od té doby, co používám Nette jsem asi nikdy hidden input nutně nepotřeboval (už vůbec ne na data typu ID záznamu, který chci editovat).

@lookass
Pokud formulář pracuje s jednou databázovou tabulkou, tak asi input hidden potřeba není, id záznamu máme v parametru akce v URL.

Formulář ale může být složitější – např, bude vedle dat uživatele z hlavní tabulky user obsahovat data z dalších – podřízených tabulek (např. několik bankovních účtů, několik platebních karet). Id záznamů podřízených tabulek bank_account a bank_card budeme při ukládání změn do databáze potřebovat, uživatel je nesmí editovat a tak je ideální je vložit jako input hidden.

Jinou možností použití input hidden je pro přenos stavu formuláře mezi requesty – např. počet záznamů dynamicky přidaných do formuláře, ješte předtím, než se uloží do databáze – továrna na formulář musí někde načíst kolik neuložených prvků by měla do formuláře vygenerovat. Stav formuláře by bylo ideální přenášet pomocí persistentních parametrů – a bezpochyby i design pattern správné řešení, nepodařilo se mě ale persistentní parametry ve formuláři rozchodit.

Editoval m.brecher (17. 3. 2021 15:53)