Pozdější naplnění selectu formulářové komponenty

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

Dobrý den, prosím o radu. Výsledkem tohoto kódu je, že hodnota formulářového pole hidden se v pořádku dostane až k metodě pro obsluhu odeslání a hodnota pole select se tam nedostane. Nemůžu zjistit proč.

class TestPresenter extends BasePresenter {

    protected function createComponentEditForm() {
        $form = new AppForm();
        $form->addHidden('id');
        $form->addSelect('ciselnik', '');
        $form->addSubmit('ulozit', 'Uložit');
        $form->addSubmit('zrusit', 'Zrušit');
        $form->onSubmit[] = array($this, 'Form_Submit');
        return $form;
    }

    public function handleEdituj($editId) {
        $ciselnik=array('1'=>'prvni', '2'=>'druhy'); // ve skutečnosti z dababáze

        $this['editForm']['id']->value = $editId;
        $this['editForm']['ciselnik']->items = $ciselnik; //<< tady je asi něco špatně, ale zobrazí se správně
    }

    public function Form_Submit(Form $form) {
        $values = $form->getValues();
        Debug::dump($values);
        // výsledek: $id == číslo, $ciselnik == NULL
    }
} // class

Select se správně včetně hodnot vykreslí, ale po odeslání formuláře se z něj nic nepřenese.
Jestliže naplnění položek selectu dám do továrničky ($form->addSelect('ciselnik', '',array('1'=>'prvni', '2'=>'druhy')) tak je všechno v pořádku. Továrnička ale nemůže vědět, co bude třeba zobrazit. To se řídí proměnnou $editId předanou teprve až handleru.

Je mi divné, že obě hodnoty jsou nastavené stejným způsobem a jedna přitom zůstane prázdná.

Patrik Votoček
Člen | 2221
+
0
-

Zkus místo $this['editForm']['ciselnik']->items = $ciselnik; použít $this['editForm']['ciselnik']->setItems($ciselnik); je tam kolem toho nějáká magie viz: https://api.nette.org/…Box.php.html#148

Obecne doporucuju pouzivaz pri vkladani dat do formu set metody… nejak jsem zapomel na syntactic sugar (mel bych jit spat)

Editoval vrtak-cz (24. 8. 2009 2:42)

jasir
Člen | 746
+
0
-

Pro nastavení defaultních hodnot formuláře nepoužívej setValue() (ani syntactic sugar ->value = ...), ale metodu setDefaults:

<?php
$this['editForm']->setDefaults( array('id'=>$editId));
//nebo alternativně (doufám):
$this['editForm']->defaults = array( 'id'=>$editId);
?>

Editoval jasir (24. 8. 2009 1:39)

Patrik Votoček
Člen | 2221
+
0
-

jasir: zrovna jsem to chtěl editem dopsat… :-D

jasir
Člen | 746
+
0
-

vrtak-cz napsal(a):

jasir: zrovna jsem to chtěl editem dopsat… :-D

Nojo, tři čtvrtě na dvě, ideální čas na soutěž kdo dřív odpoví ;-)
Jinak já myslím, že pro to setItems() může syntactic sugar použít bez problémů, žádnou magii tam nevidím… Ale je fakt, že už moc nevidím ;-)

jarks
Člen | 94
+
0
-

Díky pánové, v tuto dobu jsem odpověď nečekal.

Bohužel ->setItems má stejný výsledek, tedy žádný. A ->setDefaults je snad k něčemu jinému. Tady jde o naplnění celého selectu, nikoliv o to, která jeho položka má být vybraná. I tak jsem to zkusil

$def = array('ciselnik' => array('1'=>'prvni', '2'=>'druhy');
$this['editForm']->setDefaults($def);

ale neúspěšně.

jasir
Člen | 746
+
0
-

Mě se nějak nezdá ten název metody handleEdituj. Podle mě se při submitu nevolá, a proto je ten form rozsekanej. Proč to máš takhle nevím, ale typicky by jsi měl upravovat formulář (aby byl kompletní) před fází zpracování signálů. Například v metodě actionEdituj.

Editoval jasir (24. 8. 2009 2:11)

Patrik Votoček
Člen | 2221
+
0
-

Jasir má pravdu teď mě to nakoplo a docvaklo. Pokud odešleš formulář nette volá signál „submit“ který převezme $form->onSubmit. A tím pádem se na tvůj signál „edit“ nedostane. Editování by měla být action a né signál. Signál bych používal na něco jiného.

Jde vůvec v nette docílit volání 2 signálů při jednom požadavku??

PS: snad nepíšu blbosti…

Edit: teď mě tak napadlo že by to teoreticky šlo oblbnout asi takto ale netestoval jsem.:

$form->addSubmit('zrusit', 'Zrušit');
$form->onSubmit[] = array($this, 'handleEdit'); //<-- tenhle radek jsem pridal ale nenapada me jak mu predat parametr s IDckem...
$form->onSubmit[] = array($this, 'Form_Submit');
return $form;

Editoval vrtak-cz (24. 8. 2009 2:57)

jarks
Člen | 94
+
0
-

Díky. Ale proč u jednoho údaje ($id) k naplnění hodnoty dojde a u druhého ne? Nemůže to být nějaká chyba v Nette?

Zkusím popsat, proč to tak dělám, třeba je to špatně. Důvod je ten, že na stránce je odkaz, ten

  • zavolá signál, který obstará nastavení pomocné proměnné, která určuje zda se zobrazí odkaz nebo formulář.
  • zároveň u položek, kde je to třeba, bez problémů naplní formulář defaultními hodnotami.

Na místě odkazu se zobrazí formulář, uživatel vyplní nebo vybere, odešle a opět se zobrazí odkaz. Prostě „in place editing“. Používám ajax, ale popisovaný problém se projeví ajax neajax.

V šabloně je:

{snippet polozka span}
    {if $editItem != 'polozka'} // $editItem je pomocná proměnná
        <a href="{link handleEdituj!, 'editId' => $p->id, 'editItem' => 'polozka'}" class="ajax">{$p->nazev}</a>
    {else}
        {control editForm} // když handle nastaví pomocnou proměnnou, místo odkazu se zobrazí formulář
    {/if}
{/snippet}

Na dané stránce je tento postup bez problémů použit mnohokrát u textových položek i selectů. Problém nastal až teď, kdy potřebuji v handle naplnit celý select. Všechno se správně zobrazí, ale hodnota se nepředá. Bohužel nevím jak jinak předat komponentě ID podle kterého si má sestavit formulářový select.

Panda
Člen | 569
+
0
-

Je to jednoduché – Nette při odeslání formuláře kontroluje, jestli zadaná data vyhovují definicím (tzn. aby nešlo modifikovat odesílaná data nebo si do selectu přidat volbu, ke které by neměl mít přístup). V případě onoho selectu se kontroluje, zda zaslaná hodnota je jedna z těch, které select nabízí. Háček je v tom, že v Tebou vytvořeném formuláři select nenabízí vůbec nic. Ty sice někde ze stránky zavoláš signál a selectu se požadované možnosti přidají, ale jen pro zbytek požadavku, ve kterém je signál volán. Při odeslání formuláře se opět vytvoří jeho instance, kde je select bez vyplněných hodnot, které může nabývat, validace odeslané hodnoty tedy neprojde, a tak se zdá, že hodnoty nebyly nastaveny.

//Doplnění: u textových polí k této kontrole nedochází – formulář nemá informaci o tom, jakých hodnot může prvek nabývat. Proto jsi s nimi neměl problémy.

K vyřešení problému budeš muset trochu předělat koncept, jakým s formulářem pracuješ – jeho definice by neměla být závislá na volání nějakého signálu. Logiku je potřeba přesunout do metody action<action>().

Zmiňované přidání signálu do události onSubmit fungovat nebude – hodnoty jsou v době volání události již zpracované a ořezané. Navíc se signálu nepředá parametr $editId (resp. on se předá, ale bude obsahovat instanci AppForm místo zamýšlených dat), ve kterém, jak se zdá, celé řešení stojí.

// Poznámka: ještě zkusím dopsat návrh kódu, který by měl fungovat

// Doplnění slíbeného kódu:

TestPresenter:

<?php

class TestPresenter extends BasePresenter {


	public function actionEdit($editId = NULL, $editItem = NULL) {
		if ($editId) {
			// Zde bude podobný kód, jako v původním signálu
			$ciselnik=array('1'=>'prvni', '2'=>'druhy'); // ve skutečnosti z dababáze

			$this['editForm']['id']->value = $editId;
			$this['editForm']['ciselnik']->items = $ciselnik;
		}

		$this->template->editItem = $editItem;
	}


	protected function createComponentEditForm() {
		$form = new AppForm();
		$form->addHidden('id');
		$form->addSelect('ciselnik', '');
		$form->addSubmit('ulozit', 'Uložit');
		$form->addSubmit('zrusit', 'Zrušit');
		$form->onSubmit[] = array($this, 'Form_Submit');
		return $form;
	}

	public function Form_Submit(Form $form) {
		$values = $form->getValues();
		Debug::dump($values);
		// výsledek: $id == číslo, $ciselnik == NULL

		// Na konci handleru by měl být v ostrém nasazení redirect:
		if (!$this->isAjax())
			$this->redirect('edit');
		else
			$this->template->editItem = NULL;
			// U AJAXového odeslání formuláře bychom měli vyresetovat
			// pomocnou proměnnou, jinak by formulář zůstal stále zobrazený
	}
} // class

?>

Šablona:

{snippet polozka span}
    {if $editItem != 'polozka'}
        <a href="{link edit, 'editId' => $p->id, 'editItem' => 'polozka'}" class="ajax">{$p->nazev}</a>
    {else}
        {control editForm}
    {/if}
{/snippet}

Uvedené řešení má navíc jednu výhodu – pokud by z nějakého důvodu v původním formuláři došlo během jeho zpracování k ošetřené chybě (například validace Mime-Type u nahraného souboru, který nelze ověřit JavaScriptem, nebo pokus o vložení duplicitního ID), formulář by uživateli zmizel a nic by se nedozvěděl – nebyl by zavolán signál a proměnná $editItem by nebyla k dispozici. U řešení, které jsem napsal, uživateli formulář zmizí až v momentě, kdy je vše v pořádku odesláno a zpracováno.

Editoval Panda (24. 8. 2009 9:52)

jarks
Člen | 94
+
0
-

Díky moc. Prozatím mi to nefunguje a zdá se, že problém je v přiřazení pomocné proměnné $this->template->editItem = $editItem; v action. Není možné, že se proměnná přiřadí jiné nebo žádné proměnné, protože action neví co se vykresluje?

Template se jmenuje ‚detail‘, zkoušel jsem použít $this->setView(‚detail‘), ale ani tak nic.

//edit: Když vypnu ajax, vytvořím pokusně šablonu detail.edit.phtml, kterou po mě laděnka chce a dám do ní {dump}, správně nastavené editItem v ní vidím, ale myslím, že ajaxem do původní šablony se to nenastaví.

Editoval jarks (24. 8. 2009 14:56)

Panda
Člen | 569
+
0
-

jarks napsal(a):

Díky moc. Prozatím mi to nefunguje a zdá se, že problém je v přiřazení pomocné proměnné $this->template->editItem = $editItem; v action. Není možné, že se proměnná přiřadí jiné nebo žádné proměnné, protože action neví co se vykresluje?

Template se jmenuje ‚detail‘, zkoušel jsem použít $this->setView(‚detail‘), ale ani tak nic.

//edit: Když vypnu ajax, vytvořím pokusně šablonu detail.edit.phtml, kterou po mě laděnka chce a dám do ní {dump}, správně nastavené editItem v ní vidím, ale myslím, že ajaxem do původní šablony se to nenastaví.

Pokud se tvůj action jmenuje detail, tak si samozřejmě musíš metodu přejmenovat…

<?php
class TestPresenter extends BasePresenter {


        public function actionDetail($editId = NULL, $editItem = NULL) {
                // ...
        }


        // ...

        public function Form_Submit(Form $form) {
                // ...
                if (!$this->isAjax())
                        $this->redirect('detail');
		// ...
        }
} // class
?>

Template se pak bude jmenovat detail.phtml a bude umístěný ve složce templates/Test (podle TestPresenter).

Uvedený kód mi fungoval (včetně AJAXu).

// Doplnění: jinak teď jsem si ještě všiml, že snippet balíš do prvku span. To není moc dobré, vzhledem k tomu, že span je řádkový element a form blokový. Doporučuji span změnit na div.

Editoval Panda (24. 8. 2009 15:19)

jarks
Člen | 94
+
0
-

Panda napsal(a):
Pokud se tvůj action jmenuje detail, tak si samozřejmě musíš metodu přejmenovat…

Už to konečně funguje, jen kvůli ajaxu jsem k action ještě musel přidat

$this->validateControl();
$this->invalidateControl('polozka');

Dobře a srozumitelně jste mi už po několikáté poradil. Jsem začátečník a moc si toho vážím.

// Doplnění: jinak teď jsem si ještě všiml, že snippet balíš do prvku span. To není moc dobré, vzhledem k tomu, že span je řádkový element a form blokový. Doporučuji span změnit na div.

Uvědomuji si to, ale jde o položky v tabulce a potřebuji aby formulář zůstával v řádku.