Posloupnost onSuccess vs. vytvoření komponenty formuláře
- RadaR
- Člen | 46
Ahoj, potřeboval bych poradit s problémem s nesprávně seřazenými
položkami z databáze, když ukládám formulář AJAXem. Celý formulář je
komponenta a zabalený do snipettu objednav a v onSuccess()
provádím $this->redrawControl('objednav');
Kreslím položky formuláře na základě jejich sloupce
priorita
v tabulce. Položky lze pomocí drag&drop přesouvat
a měnit pořadí. Pomocí JS nastavuji v hidden poli priorita
správné pořadí všech položek, sestupně 3… 2… 1.
Uložení hodnot po odeslání formuláře probíhá v onSuccess metodě. Po
uložení se hodnoty v priorita
změní správným způsobem,
jenže formulář a položky se vykreslí dle stavu před
uložením.
Nevím jak v Nette funguje posloupnost vzniku komponent a zprocesování onClick / onSuccess, ale jeví se mi to tak, že dojde napřed k vytvoření komponenty formuláře – createComponentObjednavkaForm() (kde se načtou data z DB v původním stavu, před zavoláním UPDATE sql) a teprve až pak se provede uložení změn do tabulky v DB – ObjednavkaFormSuccess(). Tím pádem JSON data, vrácená AJAXem, potažmo pořadí položek, mají původní stav, před uložením (ověřeno). Pokud pak stránku dodatečně obnovím F5, potom je vše v pořádku.
Domnívám se, že potřebuju v té onSuccess() metodě nějak znovu zavolat vytvoření komponenty formuláře, aby se znovu načetly data z tabulky po uložení. Nebo nějak změnit přímo objekt formuláře a ty komponenty v něm.
Data z DB se načítají přímo v té komponentě formuláře. Zkoušel jsem data načítat i v Action() a Render() metodě, ale nemělo to na nic vliv.
Poradí někdo, kde je háček? Už si absolutně nevím rady. Díky.
Editoval RadaR (7. 1. 2017 12:11)
- ViPEr*CZ*
- Člen | 817
Ano, formulář se nejprve sestaví. To aby Nette vědělo, která data
z POST vůbec má zpracovávat a jak. Protože pak na druhou stranu
v onSuccess používáte zajísté $form->getValules() a to by nemohlo
právě fungovat.
Btw tohle by jste mohl vyřešit tak, že si form vykreslíte ručně. Prvky si
seřadíte podle priorita. Jen to bude vyžadovat, aby DB věděla u každého
řádku control name a musíte zajistit, aby toto name obsahoval form. Toť
jedno možné řešení.
- RadaR
- Člen | 46
Values mi do onSuccess „přitečou“ parametrem z
function ObjednavkaFormSuccess ($form, $values)
ale to je asi
jedno, jestli volám $form->getValues()
nebo pracuji s
$values
. Ale chápu, kam míříte a uvedený důvod.
Můj formulář je celkem složitý a ručně už jej (pokud myslíme to samé?) kreslím. Obsahuje dva kontejnery (hlavičku a položky) přičemž položky obsahují ještě další kontejnery (technologie položek, těch může být 1:n).
Mohl byste prosím svoji radu trošku více rozepsat? Možná nějaký kousek kódu, pro příklad. Přiznám se, že to úplně nechápu.
A nešlo by nějak „vynutit“, na správném místě, znovuvytvoření té komponenty formuláře? Nebo těch položek v něm?
Hodím sem osekanou šablonu a presenter, snad se v tom půjde dát vyznat:
<?php
{block content}
<h1>Objednávka č. <strong>{$article->cobj}</strong></h1>
<hr>
{snippet objednav}
{form objednavkaForm}
{formContainer objednavka}
{label id_zakaznik /}
{input id_zakaznik class => 'form-control'}
{input expres}{label expres /}
{label poznamka_int /}{input poznamka_int class => 'form-control'}
{/formContainer}
<hr>
{formContainer objpol}
<h2>Položky <span class="btn btn-primary" id="product-add-new"><span class="glyphicon glyphicon-plus"></span> Přidat novou položku</span></h2>
<div class="products">
{if count($formContainer->getComponents()) > 0}
{foreach $formContainer->getComponents() as $idObjpol => $fieldObjpol}
{formContainer $idObjpol}
<!-- produkt -->
<div class="product" id="objpol_{$idObjpol}" data-id="{$idObjpol}">
<div class="handle"></div>
{input dcera}
{input priorita}
{input deleteobjpol class => 'btn btn-danger btn-xs'}
<div class="col-md-4">
{formContainer vyrtechgroup_$idObjpol}
{foreach $formContainer->getComponents() as $idVyrtech => $fieldVyrtech}
{formContainer $idVyrtech}
<div class="row technology" data-id="{$fieldVyrtech['id_vyrtech']->getHtmlId()}">
<div class="col-md-5 technology-head">{label id_vyrtech /} {input id_vyrtech}</div>
<div class="col-md-4">{input datum_vyhotoveni class => 'form-control'}</div>
<div class="col-md-3"><div class="checkbox">{label hotovo /} {input hotovo}</div></div>
</div>
{/formContainer}
{/foreach}
{/formContainer}
</div>
<!-- /produkt -->
{/formContainer}
{/foreach}
{/if}
<div class="products-group empty"></div>
</div>
{/formContainer}
<div class="text-right">
{input btn_updateclose class => "btn btn-default"}
{input btn_update class => "btn btn-default"}
</div>
{/form}
{/snippet}
?>
Presenter a komponenta formu:
<?php
protected function createComponentObjednavkaForm()
{
$form = new Form;
$form->getElementPrototype()->class('ajax'); // přidá formuláři class="ajax"
$form->setRenderer(new BootstrapVerticalRenderer);
$objednavkaContainer = $form->addContainer("objednavka");
$zakaznici = $this->dataManager->getZakaznik();
if (count($zakaznici) > 0):
foreach ($zakaznici as $values):
$zakaznik[$values['id_zakaznik']] = $values['jmeno_prijmeni'];
endforeach;
else:
$zakaznik[0] = 'neurčeno';
endif;
$objednavkaContainer->addSelect('id_zakaznik', 'Zákazník', $zakaznik)
->setRequired(TRUE)
->setDefaultValue($this->objednavkaRow->id_zakaznik);
$objednavkaContainer->addCheckbox('expres', 'Expresní objednávka')
->setDefaultValue($this->objednavkaRow->expres);
$objednavkaContainer->addTextArea('poznamka_int', 'Poznámka - interní')
->setDefaultValue($this->objednavkaRow->poznamka_int);
// načtu si výrobní technologie
$vyrtech = $this->dataManager->getVyrtech();
// načtu si položky objednávky
$rows = $this->dataManager->getObjpol($this->getParameter('id'));
// formContainer pro položky objednávky
$objpolContainer = $form->addContainer("objpol");
foreach ($rows as $objpol):
$fieldsObjpol = $objpolContainer->addContainer($objpol->id_objpol);
$fieldsObjpol->addHidden('dcera', 'Dcera')
->setDefaultValue($objpol->dcera);
$fieldsObjpol->addText('priorita', 'Priorita')
->setDefaultValue($objpol->priorita);
$fieldsObjpol->addSubmit('deleteobjpol', 'Smazat')
->onClick[] = [$this, 'DeleteObjPol'];
$objtech = array();
foreach ($objpol->related('objtech.id_objpol', 'objtech.id_objpol') as $item):
$objtech[$item->id_vyrtech] = array('hotovo' => $item->hotovo, 'datum_vyhotoveni' => $item->datum_vyhotoveni);
endforeach;
// formContainer pro výrobní technologie
$vyrtechContainer = $fieldsObjpol->addContainer("vyrtechgroup_". $objpol->id_objpol);
foreach ($vyrtech as $item):
$fieldsVyrtech = $vyrtechContainer->addContainer($item->id_vyrtech);
$fieldsVyrtech->addCheckbox('id_vyrtech', $item->nazev)
->setDefaultValue(isset($objtech[$item->id_vyrtech]) ? 1 : 0);
$fieldsVyrtech->addText('datum_vyhotoveni', 'Datum vyhotovení')
->setDefaultValue(isset($objtech[$item->id_vyrtech]['datum_vyhotoveni']) ? $objtech[$item->id_vyrtech]['datum_vyhotoveni']->format('d.m.Y') : '')
->setRequired(FALSE)
->setAttribute('data-date', '1')
->setAttribute("placeholder", "dd.mm.rrrr")
->addRule($form::PATTERN, "Datum musí být ve formátu dd.mm.rrrr", "(0[1-9]|[12][0-9]|3[01])\.(0[1-9]|1[012])\.(19|20)\d\d")
->addFilter(function ($value) { return trim($value) ? Nette\Utils\DateTime::from($value) : null; })
->setNullable();
$fieldsVyrtech->addCheckbox('hotovo', 'Hotovo')
->setDefaultValue(isset($objtech[$item->id_vyrtech]) and $objtech[$item->id_vyrtech]['hotovo'] == 1 ? 1 : 0);
endforeach;
endforeach;
$form->addSubmit('btn_update', 'Uložit objednávku');
$form->addSubmit('btn_updateclose', 'Uložit a zavřít');
$form->onSuccess[] = [$this, 'ObjednavkaFormSuccess'];
return $form;
}
?>
Editoval RadaR (7. 1. 2017 13:10)
- F.Vesely
- Člen | 369
Podle API dokumentace funkce getComponents()
vrati
ArrayIterator
, ktery ma funkci uasort($cmp_function)
.
Takze teoreticky neco jako toto, by mohlo fungovat, ale nemam to otestovane:
$formContainer->getComponents()->uasort(function($a, $b){
return $a['priorita']->getValue() - $b['priorita']->getValue();
});
- RadaR
- Člen | 46
2 F.Vesely:
To vypadá hodně slibně! Velmi elegantně a jednoduše by to řešilo můj problém. Jenže bohužel to nic nedělá :( Kód jako takový proběhne bez chyb, ale když si vypíšu komponentu předtím a potom, pořadí se vůbec nezmění.
Provádím to v onSuccess metodě a zkoušel jsem to i mírně modifikovat, takto:
<?php
Debugger::barDump($form['objpol']->getComponents());
$form['objpol']->getComponents()->uasort(function($a, $b) {
return ($a['priorita']->GetValue() > $a['priorita']->GetValue() ? 1 : -1);
});
Debugger::barDump($form['objpol']->getComponents());
?>
ale pořadí je v obou dumpech v TRACY stejné… Nepřehlédnul jsem
něco? Předpokládám, že ->getComponents()
vrací a pracuje
přímo s instancí toho objektu, nevytváří kopii, že ano?
Zkusil jsem $test = $form['objpol']->getComponents()
a v
$test
bylo pouze TRUE
.
Díky za každé pošťouchnutí…
- RadaR
- Člen | 46
Dal jsem na zkoušku tvůj kód do šablony. Umístil jsem jej před iteraci položek v kontejneru (úplnější šablona – viz můj post šablony výše), ale ani zde to bohužel nefunguje. Kód projde, chybu to nehlásí, ale na změnu pořadí to nemá žádný vliv.
Nejsem si jist, jestli v tom okamžiku, kdy se vykreslují data šablonu pro JSON, už jsou v šabloně ta správná data. Jestli se nepracuje pořád s těmi před uložením do DB. Tudíž by to celý nemělo smysl na tomnto místě řešit.
<?php
...
{if count($formContainer->getComponents()) > 0}
{php $formContainer->getComponents()->uasort(function($a, $b) { return $a['priorita']->getValue() - $b['priorita']->getValue(); })}
{foreach $formContainer->getComponents() as $idObjpol => $fieldObjpol}
{formContainer $idObjpol}
...
?>
Editoval RadaR (7. 1. 2017 18:47)
- RadaR
- Člen | 46
To už je asi moc velká divočina…
Parse Error syntax error, unexpected '{'
. A stejně potřebuju
iterovat klíč ⇒ hodnota takže tam potřebuju i to
as $idObjpol => $fieldObjpol
a to když zkusím
<?php
{foreach $formContainer->getComponents()->uasort(function($a, $b) { return $a['priorita']->getValue() - $b['priorita']->getValue(); }) as $idObjpol => $fieldObjpol}
?>
tak dostanu jinou chybu
Invalid argument passed to foreach; array or Traversable expected, boolean given.
- CZechBoY
- Člen | 3608
Uasort asi nebude metoda ale php funkce…
Dej ty komponenty jako prvni parametr, callback jako druhy.
http://php.net/…n.uasort.php
edit: uasort je metoda ArrayIterator.
Editoval CZechBoY (7. 1. 2017 22:00)
- RadaR
- Člen | 46
2 F.Vesely
Tohle už opravdu funguje! :) Díky moc. Já v mezičase přišel na to, jak to řešit v onSuccess události formuláře. Takže ať si každý zváží, co mu víc vyhovuje. Hodím sem obě varianty.
- Řešení v šabloně
<?php
...
{var $components = $formContainer->getComponents()}
{php $components->uasort(function($a, $b){ return $b['priorita']->getValue() - $a['priorita']->getValue();})}
{foreach $components as $idObjpol => $fieldObjpol}
...
?>
- Řešení v onSuccess události formuláře (naliju si jednotlivé kontejnery položek do array, odstraním je z komponenty, seřadím pole dle potřeby a vrátím zpátky do komponenty)
<?php
...
$components = $form['objpol']->getComponents();
$components->uasort(function($a, $b){ return $b['priorita']->value - $a['priorita']->value;});
foreach ($components as $key => $value):
$form['objpol']->removeComponent($form['objpol'][$key]);
$form['objpol']->addComponent($value, $key);
endforeach;
...
?>
Editoval RadaR (7. 1. 2017 20:35)