Závislé inputy – Best practise

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

Ahoj,
o závislých select-boxech apod. se tu toho psalo už hodně. Žádné řešení ale podle mě není ideální a každé způsobuje v praxi nějaké problémy.

Příklad

Mějme jednoduchý formulář pro ukládání odkazu v menu:

class LinkForm extends Form
{

	public function __construct($parent = NULL, $name = NULL)
	{
		parent::__construct($parent, $name);

		$this->init();
	}


	public function init()
	{
		$this->addText('name', 'Název odkazu')
			->setRequired('Pole %label musí být vyplněno.');

		// Link type
		$this->addSelect('link_type', 'Typ odkazu', array(
			'article' => 'Článek',
		))->setPrompt('- vyberte -');
		$this->addSubmit('choose_type', 'Vybrat')
			->setValidationScope(FALSE);

		// Link value (depends on 'Link type')
		if ($this['link_type']->value == 'article') {
			$articlePairs = $this->articleService->getPairs();

			$this->addSelect('link_value', 'Název článku', $articlePairs);
		}

		$this->addSubmit('save', 'Uložit');
	}

}

Takto napsaný formulář bude fungovat. Aspoň tedy v akci pro vytváření odkazu. V editaci odkazu, kde potřebuji formulář nejdříve naplnit defaultními daty, ale fungovat nebude:

public function renderEdit($id)
{
	$link = $this->linkService->get($id);

	$this['linkForm']->setDefaults(array(
		'name' => $link->name,
		'link_type' => $link->link_type,
		'link_value' => $link->link_value,  // polozku 'link_value' formular v soucasne chvili nezna
	));
}

Řešení

  1. Přetížit ve třídě LinkForm metodu setDefaults a položku ‚link_value‘ k formuláři přidávat i zde.
    • Problém: Když v metodě setDefaults přidám k formuláři nějakou položku, tak ta se objeví na konci formuláře, tzn. ZA odesílacím tlačítkem.
    • Problém: Co když místo $form->setDefaults použiju $form[link_type]->setDefaultValue()?
  2. Hodnoty z formuláře ukládat místo setDefaults do sessions. Formulář pak na základě hodnot ze sessions přidá závislé inputy a až PAK sám na sebe zavolá setDefaults($sessions->formValues);
    • Problém: Nepřijde mi to vůbec jako hezké nebo čisté řešení.
    • Problém: Co když budu mít v prohlížeči ve dvou záložkách otevřený stejný formulář, ale každý s jinými daty?

Tedy, žádné řešení není ideální a nic dalšího mě už nenapadá.

Nějaké tipy, rady, názory? Díky.

Editoval nanuqcz (3. 1. 2014 15:43)

Robyer
Člen | 74
+
0
-

Já bych to přidávání link value oddělil do samostatné metody, kterou bys pak mohl zavolat i přímo z presenteru. Ta by zkontrolovala hodnotu link_type a naplnila ten select link_value správnými daty. (Nebo by ten select úplně odstranila, pokud by link_type neměl správnou hodnotu)

nanuqcz
Člen | 822
+
0
-

Robyer: Jak by v takovém případě vypadalo naplňování formuláře defaultními hodnotami?

// naplnim zakladni inputy
$this['linkForm']->setDefaults(array(
    'name' => $link->name,
    'link_type' => $link->link_type,
));

// pridam 'link_value' input
$this['linkForm']->updateLinkValueInput();

// naplnim 'link_value'
$this['linkForm']->setDefaults(array(
    'link_value' => $link->link_value,
));

Takto složitý kód jen pro naplnění formuláře? Navíc, takto se ‚link_value‘ přidá na konec formuláře, až za odesilací tlačítko.

Editoval nanuqcz (3. 1. 2014 18:19)

Robyer
Člen | 74
+
0
-

Ano, nějak tak jsem to myslel. Pro tohle všechno si můžeš vytvořit třeba metodu setMyDefaults(…) ve formuláři… teď si uvědomuju, že to je vlastně podobné, jako tvé řešení #1.

Co se týče problému s přidáváním link_value na konec formuláře, tak bych ho v konstruktoru (resp. ve tvém init()) vytvářel vždy. A při zavolání updateLinkValueInput() ho pak naplnil daty nebo úplně odstranil.

Editoval Robyer (3. 1. 2014 19:00)

David Matějka
Moderator | 6445
+
0
-

premyslim a nenapada me zadny elegantni reseni. ale co takhle pridat do BaseControlu onUpdate event volany pri kazdem setValue (tedy i pri setDefaultValue)? pak by to bylo hezky

$this->addSelect('link_type', 'Typ odkazu', array(
	'article' => 'Článek',
))->setPrompt('- vyberte -')->onUpdate[] = function($input) {
	if ($input->value == 'article') {
            $articlePairs = $this->articleService->getPairs();
            $this->addSelect('link_value', 'Název článku', $articlePairs);
        }
});
nanuqcz
Člen | 822
+
0
-

matej21: Kdyby se něco takového přidalo do Nette, byl bych rád.

Zároveň by se mi pak líbilo, kdyby v Nette\Forms byla možnost přesouvání prvků:

$form['link_type']->onChange[] = function($input){
	// Ugly :-(
	$form = $input->form;
	$newSelect = new SelectBox(...);
	$form->addComponent($newSelect, ..., $form['save']);
};
$form['link_type']->onChange[] = function($input){
	// Nice :-)
	$form = $input->form;
	$form->addSelect(...)
	    ->moveBefore($form['save']);
}

Mám sepsat Feature Request, nebo RFC?

EDIT: RFC sepsáno

Editoval nanuqcz (13. 1. 2014 2:58)

Mesiah
Člen | 240
+
0
-

Možná plácnu hloupost, ale kdybys selecty měl v containeru a další selecty přidával do tohoto containeru, nevyřešilo by to problém s renderingem selectu za buttonkem?

nanuqcz
Člen | 822
+
0
-

Mesiah: Skoro, musel bych je přidávat do stejné grupy ($form->addGroup(...)) :-) To je ale jen vedlejší problém. Hlavní problém v tomhle vlákně ale je, jak to udělat, aby se závislý input zobrazoval jak v závislosti na odeslaných hodnotách, tak v závislosti na nastavených hodnotách v kódu ($form->setDefaults(...)).