propojení DependentSelectboxu a formuláře s dynamickým počtem prvků

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
twingosh
Člen | 10
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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í dat getSpecificationValues
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
+
0
-

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 :-(

Foowie
Člen | 269
+
0
-

Máš to mít . (Potřebovalo to pár drobných úprav takže aktualizovat obě komponenty ;)

twingosh
Člen | 10
+
0
-

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.

Foowie
Člen | 269
+
0
-

IE bug fixed.