propojení DependentSelectboxu a formuláře s dynamickým počtem prvků
- twingosh
- Člen | 10
Zdravím,
pokouším se propojit DependendSelectBox a formulář s dynamickým počtem prvků (řešený Zde:https://forum.nette.org/…y-pocet-poli).
Obě featury mi fungují samostatně výborně. Problém nastává, když se
je pokusím propojit.
Řeším klasický problém editace uživatelů, kdy si každý uživatel může
nastavit vzdělání výběrem kategorie a pak z DependendSelectboxu výběrem
konkrétního typu vzdělání. Jakmile si vyberu první školu a chci přidat
další řádek, přerenderuje se mi znova celý form.
Aby mi zůstaly zachovány již vyplněné hodnoty používám
$this['poolForm']->setDefaults($session->poolForm);
jak je uvedeno v příkladu.
A v tomto je problém, v $session->poolForm jsou uloženy všechny hodnoty, ale setDefault() je nastaví všem prvkům, krom prvkům dependendSelectboxu. Vyjímka je, jen když v prvním (parent) selectboxu nastavena první hodnota, pak se přes setDefault() nastaví i hodnota DependentSelectBoxu
Tady je ta kompenta:
{
$form = new AppForm($this, $name);
$session = Environment::getSession('poolForm');
if (isset($session->poolForm['educationCount'])) {
$educationCount = $session->poolForm['educationCount'];
}
else {
$educationCount = 1; // defaultni pocet odpovedi
}
$form->addHidden('educationCount')->setValue($educationCount);
$model = new \SelectiveProcedureModel();
$arr = $model->getEducationCategories();
for ($i = 1; $i <= $educationCount; $i++) {
$form->addSelect('category'.($i),'Category'.($i),$arr);
$form->addDependentSelectBox("specification".($i), "Specification".($i), $form["category".($i)], array($this, "getSpecificationValues"));
if($this->isAjax())
$form["specification".($i)]->addOnSubmitCallback(array($this, "invalidateControl"), "poolForm");
}
$form->addSubmit('educationAdd', 'add Education')
->setValidationScope(NULL)
->onClick[] = array($this, 'handleAddEducation');
$form->addSubmit('educationDel', 'Delete Education')
->setValidationScope(NULL)
->onClick[] = array($this, 'handleDeleteEducation');
$form->addSubmit('save', 'Uložit')->onClick[] = callback($this, 'poolFormSubmitted');
$form->addSubmit('cancel', 'Zrušit')->setValidationScope(NULL);
return $form;
}
šablona je stejná jako v příkladu:
{snippet poolForm}
{widget poolForm}
{/snippet}
Callback je stejný jako v příkladu u addonu dependentselectboxu.
Už se s tím hraju několikátý večer a pročetl jsem tady toho spoustu, ale řešení jsem nenašel.
Problém vidím pravděpodobně ve volání callbacku u dependentSelectboxu při odeslání formuláře pro přidávání dalšího inputu.
Ještě dodám, že používám poslední verzi nette i všech js.
Děkuji za tipy.
- Foowie
- Člen | 269
Pošli sem dump z $session->poolForm
,
$model->getEducationCategories()
a metody
getSpecificationValues
a handleAddEducation
. Nějak se
mi nedaří nasimulovat ta chyba ;)
Jinak https://forum.nette.org/…rmcontaineru
Editoval Foowie (3. 12. 2010 18:50)
- twingosh
- Člen | 10
tohle je v session->poolForm:
array(7) {
"educationCount" => 2
"category1" => "3"
"specification1" => "56" (2)
}
tohle se nastavi po setDefaults (už se tam přidá další prvek formuláře):
array(9) {
"educationCount" => "2"
"category1" => "3"
"specification1" => NULL
"category2" => 1
"specification2" => NULL
}
Tady jsou zbylé metody:
metoda v modelu pro získání kategorií a pro získání specifikací (závislé na kategoriích):
public function getEducationCategories() {
$q = $this->connection->query('SELECT id,educationLevel FROM `education`');
return $q->fetchPairs();
}
public function getEducationSpecifications($id) {
$q = $this->connection->query('SELECT id, educationSpecificationName FROM `educationspecifications` WHERE education_id=%i', $id);
return $q->fetchPairs();
}
Přidávání položky formuláře (dalšího dependentSelectboxu):
public function handleAddEducation()
{
$session = Environment::getSession('poolForm');
$session->poolForm = $this['poolForm']->getValues();
$session->poolForm['educationCount']++;
if(!$this->isAjax()) {
$this->redirect('this', array('keepData' => TRUE));
}
$this->invalidateControl('poolForm');
}
callback, který načte do potřebného DependentSelecboxu data, na základě parent selectboxu:
public function getSpecificationValues($form) {
$model = new \SelectiveProcedureModel();
$a = $form->getValues();
$cat = array();
foreach ($a as $key=>$val) {
if(\preg_match('/^category/', $key)) {
$cat[] = $key;
}
}
$session = Environment::getSession('poolForm');
$element = \array_pop($cat);
$id = $form[$element]->getValue();
$arr = $model->getEducationSpecifications($id);
return $arr;
}
jinak oba modely vrací čistě pole, takže si to naplň z libovolným
polem …
a co se týče tvého dynamicFormContaineru, tak se mi ho nepodařilo rozjet
společně s DependentSelecboxem, ale ještě na to sednu.
Editoval twingosh (3. 12. 2010 21:20)
- Foowie
- Člen | 269
Ta metoda getSpecificationValues
se mi nechce nějak líbit.
Celé to probíhá nějak takhle:
- v
action
metodě se volá $this[‚poolForm‘]->setDefaults($session->poolForm); - což má za následek pokus o vytvoření komponenty
poolForm
- ta se vytvoří s tím, že (já) nevím, jaké se tam nastrkají hodnoty, pravděpodobně ty odeslané, nebo žádné, pokud form ještě nebyl submitlý
- nyní se provede druhá část příkazu v bodě 1 $this[‚poolForm‘]->setDefaults($session->poolForm);
- ta postupně strká do všech form. komponent hodnoty
- … začne
educationCount
,category1
, zatím vše OK - pak ale přijde na řadu
specification1
… a komponenta zavolá pro získání datgetSpecificationValues
public function getSpecificationValues($form) {
$model = new \SelectiveProcedureModel();
$a = $form->getValues();
/*
$a == array(
"educationCount" => "2"
"category1" => "3"
"specification1" => "56"
"category2" => NULL
"specification2" => NULL
);
*/
$cat = array();
foreach ($a as $key=>$val) {
if(\preg_match('/^category/', $key)) {
$cat[] = $key;
}
}
// $cat == array("category1", "category2");
$session = Environment::getSession('poolForm'); // ???
$element = \array_pop($cat); // fail ... $element == "category2"
$id = $form[$element]->getValue(); // $id == NULL
$arr = $model->getEducationSpecifications($id); // bad luck, no record :(
return $arr;
}
Edit: aby se ti lépe dolovaly data, přidal jsem jako druhý parametr metody pro získávání dat jméno DependentSelectBoxu. Tak si stáhni novou verzi z GitHubu ;)
Editoval Foowie (3. 12. 2010 22:12)
- twingosh
- Člen | 10
Díky za pomůcku, ale stále visím na mrtvém bodě :-(
Dostal jsem se do fáze, kdy mi funguje jen jedna ze dvou potřebných variant.
Pokud v callbacku dostanu $id parent selectboxu takto:
$id = = $form[$element]->getValue();
Funguje mi to jen do té doby, než odešlu formulář(chci přidat další
řádek).
(Funguje = do $id se dostane správné id nadřazeného selectboxu). Jakmile
formulář odešlu. Nastaví se $id na první prvek parent selectboxu.
Pokud se v callbacku dostanu na $id parent selectboxu takto:
$session = Environment::getSession('poolForm');
$id = (isset($session->poolForm[$element])) ? $session->poolForm[$element] : $form[$element]->getValue();
Při odeslání formuláře se mi $id nastaví na správný prvek a zobrazí
se i default hodnota dependentSelectboxu.
Když chci ale hodnotu změnit, tak si to pořád bere ze session. Takže tohle
řešení funguje jen napůl:-)
Problém je v nastavení defaultních hodnot, které při přidání prvku
musím nějak udělat.
Nevím kde je mám správně nastavovat, aby se dependentSelectbox dostal ke
svému správnému rodiči…
Nejlepší řešení by bylo, kdyby Foowie dokázal propojit své dvě udělátka DynamicFormContainer a DependentSelectBox :-) Za to bych mu byl moc vděčný :-)
Už tohle řeším asi týden, tak prosím za jakoukoliv pomoc :-(
- twingosh
- Člen | 10
Super! Díky moc. Tohle přesně jsem potřeboval :-)
Zjistil jsem, že mi to ale nefunguje v IEčku, ale to už je zase jiný problém (jquery issue) :-)
Btw. když pak používám seskupování pomocí addGroup tak se mi componentContainery vykreslí až úplně na konci formuláře. Ale to už je jen estetická drobnost :-)
Ještě jednou moc díky Foowie.