AJAX formulář s dynamickým počtem řádků… zase

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

Tak už jsem se opět zasekl, budu vděčný za každou radu.
Mám požadavek udělat formulář, kde půjde libovolně přidat nebo smazat řádek, prohledal jsem všechny možné thready tady na foru a dospěl jsem do této fáze:

  • Mám formulář v továrničce, který si vytvoří řádky podle hodnoty uložené v session.
  • při přidávání řádku, se zvýší hodnota v session a formulář by se měl překreslit

Jenže, když kliknu poprvé na ‚Přidat řádek‘, tak se s formulářem nic nestane, hodnota v session se zvýší ⇒ jakoby se nezavolala továrnička. Když klikám dál, tak už se řádky přidávají, ale pořád ten jeden chybí. Pokud stránku obnovím, tak se zobrazí i ten chybějící… což je dost nemilé.
Možná mi něco důležitého uchází… nevím, každopádně už nevím co s tím.

takhle nejak vypada tovarnicka:

<?php
public function createComponentShopForm()
{
	$namespace = Environment::getSession('products');
	if( !isset($namespace->products))
	{
		$namespace->products = 1;
	}

	$form = new AppForm;
	$form->getElementPrototype()->class('ajax');
	$form->addSubmit('odesli', 'Pokračovat');
	$form->addSubmit('storno', 'Zrušit')->setValidationScope(NULL);
	$form->addSubmit('addRow', 'Přidat další řádek')->setValidationScope(NULL);
	$default = array();
	for ( $i = 1; $i <= $namespace->products; $i++ )
	{
			$form->addGroup('Produkt č.'.$i.':');
			...
			$default['count'.$i] = 1;
	}
	$form->setDefaults($default);
	$form->onSubmit[] = callback($this, 'processShopForm');
	return $form;
}
?>

A tady je invalidace formulare:

<?php
	public function processShopForm($form)
	{
		if( $form['odesli']->isSubmittedBy())
		{
			...
			$this->redirect('this');
		}
		elseif ( $form['addRow']->isSubmittedBy())
		{
			$namespace = Environment::getSession('products');
			if ( !isset($namespace->products))
			{
				$namespace->products = 1;
			}
			else
			{
				$namespace->products+=1;
			}

			if( !$this->isAjax())
			{
				...
				$this->redirect('this');
			}
			else
			{
				...
				$this->invalidateControl('shop');
			}
		}
		else
		{
			...
			$this->redirect('Default:');
		}
	}
?>

Děkuji všem :)

Ola
Člen | 385
+
0
-

Jak ten formulář vykresluješ? Předáváš ho do šablony nebo přes widget?

pako3
Člen | 18
+
0
-

Přes widget…
Zavináče jsou ok, to už jsem procházel několikrát. :)

Ola
Člen | 385
+
0
-

Oh, myslím, že vím proč tomu tak je. Ta továrnička se zavolá, ale již při volání signálu submit, tj. příliš brzy. Pak se form připojí k presenteru a zavolá se signál submit, který zvýší hodnotu session (pozdě).

Zkus po tom zvýšení session zavolat unset($presenter["shopForm"]);

Editoval Ola (10. 5. 2010 19:53)

pako3
Člen | 18
+
0
-

Hmm, takže v tom je problém…

Když tam dám unset(…), tak už se ten formulář neinvaliduje… asi bych ho měl pak do šablony poslat znova, pokud to správně chápu?

Edit: ještě mě napadlo, zkusit „Přidat řádek“ dát mimo ten formulář, jako normální odkazy, aby posílaly ajaxové signály. Ale to nevím jestli by byla správná cesta, chovalo se to víceméně stejně. :D

Díky ;)

Editoval pako3 (10. 5. 2010 22:57)

pako3
Člen | 18
+
0
-

Děkuji ‚Ola‘ za pomoc, je to vyřešeno.

Takže kdyby někdo měl stejný problém:

zrušil jsem widget v šabloně a vypisuji formulář normálně:

<?php
{!$shopForm}
?>

a při odeslání požadavku na přidání řádku se formulář vymaže z presenteru jak psal Ola a znovu vytvoří:

<?php
unset($presenter['shopForm']);
//... zvysit pocet radku v session
$this->template->shopForm = $this->createComponentShopForm();
?>

Už to šlape jak má, díky :)

Ola
Člen | 385
+
0
-

Ne, takhle ne, to nemusí fungovat. Změň to zpět na widget a místo $presenter["shopForm"] zkus $this["shopForm"]

pako3
Člen | 18
+
0
-

Aha, tak unset($this[‚shopForm‘]); už funguje…
Díky moc :)

pako3
Člen | 18
+
0
-

Nechtěl jsem zakládat nové téma, je to velmi podobný problém…

Mám takový jednoduchý editační formulářek, který se objeví po kliknutí na ‚Upravit‘:
Tohle funguje naprosto v pořádku:

<?php
{snippet name2}
{if $editName === 1}
{widget editNameForm}
{else}
 ... vypíše normálně data
{/if}
{/snippet}
?>

A teď se uplně stejným postupem snažím docílit takového editačního seznamu, kde jsou vypsány položky a u každé je možnost ji smazat nebo upravit. Bez ajaxu to funguje pekně:

<?php
{snippet productTable}
<div><table>
<tr> ... nadpisy ... </tr>
{for $i=1; isset($data1['name'.$i]); $i++}
{if $editProduct == $i}
{widget editProductForm}
{else}
<tr> ... vypíše řádek s daty položky... </tr>
{/if}
{/for}
</table></div>
{/snippet}
?>

Ale s ajaxem to nejede. PHP strana se mi zdá ok, protože to vrátí JSON se správným obsahem ( ráno ho sem přidám ), ale editovaný řádek se, namísto zobrazení formuláře, nevykreslí vůbec. Takže vidím chybu na straně JS, ale bohužel nejsem s JS velký kamarád, takže bych poprosil někoho znalého o radu.

Používám klasicky jQuery, jquery.nette v0.2, jquery.ajaxform v0.1 a ajax.js s tímto obsahem:

<script>
$("a.ajax, .paginator a").live("click", function (event) {
	event.preventDefault();
	$.get(this.href);
});

/* AJAXové odeslání formulářů s třídou ajax */
$("form.ajax").live("submit", function () {
	$(this).ajaxSubmit();
	return false;
});

$("form.ajax :submit").live("click", function () {
	$(this).ajaxSubmit();
	return false;
});
</script>

Děkuji předem za jakékoli nápady :)

Editoval pako3 (21. 5. 2010 1:48)

despiq
Člen | 320
+
0
-

urcite bys asi nemel mit {widget editProductForm} v iteraci
to pak vytvari trable, protoze ty formulare maj stejny nazvy

pako3
Člen | 18
+
0
-

To máš určitě pravdu, předělám to, ale zatím jde pouze o případ, že je tam formulář jen jeden. Takže by se s ničím neměl hádat, aspoň myslím.

despiq
Člen | 320
+
0
-

zavinace mas predtim spravne?

pako3
Člen | 18
+
0
-

Zavináče by měly být ok, toto je celá šablona:

<?php
@{block #content}
<span> Hlavni nadpis</span>
<br />
<span> popis... </span>
<br />
{snippet flashmes}
{foreach $flashes as $flash}<div class="flash {$flash->type}">{$flash->message}</div>{/foreach}
{/snippet}

{snippet name2}
{if $editName === 1}
{widget editNameForm}
{else}
<div> ... vypíše data..</div>
{/if}
{/snippet}

<br />
<div>... nejake text ... </div>
<br />

{snippet productTable}
<div><table>
<tr> ... nadpisy...</tr>
{for $i=1; isset($data1['name'.$i]); $i++}
{if $editProduct == $i}
{var form[$i] => $presenter['editProductForm']}
{control $form[$i] begin}
... vypíše formulář
{control $form[$i] end}
{else}
<tr> ... vypíše řádek s daty ... </tr>
{/if}
{/for}
</table></div>
{/snippet}
{widget shopForm3}
?>

snippet ‚name2‘ funguje bez problemu…
snippet ‚productTable‘ se špatně aktualizuje.
Tady je JSON, který mi vrátí phpko:

{"state":[],"snippets":{"snippet--productTable":"<div><table>\n<tr><td>N\u00e1zev produktu<\/td><td>Specifikace produktu<\/td><td>Internetov\u00e1 adresa detailu zbo\u017e\u00ed (URL)<\/td><td>Po\u010det (ks)<\/td><td>Cena za kus ($)<\/td><td>Cena celkem ($)<\/td><\/tr>\n<form action=\"\/mailbox\/shop-for-me\/step-three\/?do=editProductForm-submit\" method=\"post\" id=\"frm-editProductForm\" class=\"ajax\" onsubmit=\"return nette.validateForm(this)\"><tr><td><label class=\"required\" for=\"frmeditProductForm-name\">N\u00e1zev produktu<\/label><br \/><input type=\"text\" class=\"text\" name=\"name\" id=\"frmeditProductForm-name\" value=\"sadwds\" \/><\/td><td><label class=\"required\" for=\"frmeditProductForm-specs\">Specifikace produktu<\/label><br \/><input type=\"text\" class=\"text\" name=\"specs\" id=\"frmeditProductForm-specs\" value=\"akwdoakd\" \/><\/td><td><label for=\"frmeditProductForm-count\">Po\u010det (ks)<\/label><br \/><input type=\"text\" class=\"text\" name=\"count\" id=\"frmeditProductForm-count\" value=\"1\" \/><\/td><\/tr>\n<tr><td><label class=\"required\" for=\"frmeditProductForm-url\">Internetov\u00e1 adresa detailu produktu (URL)<\/label><br \/><input type=\"text\" class=\"text\" name=\"url\" id=\"frmeditProductForm-url\" value=\"dawdw.ht\" \/><\/td><td><label class=\"required\" for=\"frmeditProductForm-price\">Cena za kus ($)<\/label><br \/><input type=\"text\" class=\"text\" name=\"price\" id=\"frmeditProductForm-price\" value=\"150\" \/><\/td><td><input type=\"submit\" class=\"button\" name=\"odesli\" id=\"frmeditProductForm-odesli\" value=\"Zm\u011bnit\" \/><\/td><\/tr>\n<\/form>\n<!-- Nette Form validator -->\r\n\r\n<script type=\"text\/javascript\">\/*<![CDATA[*\/var nette=nette||{};nette.getValue=function(a){if(a){if(!a.nodeName){for(var b=0,d=a.length;b<d;b++)if(a[b].checked)return a[b].value;return null}if(a.nodeName.toLowerCase()===\"select\"){b=a.selectedIndex;var c=a.options;if(b<0)return null;else if(a.type===\"select-one\")return c[b].value;b=0;a=[];for(d=c.length;b<d;b++)c[b].selected&&a.push(c[b].value);return a}if(a.type===\"checkbox\")return a.checked;return a.value.replace(\/^\\s+|\\s+$\/g,\"\")}};\nnette.getFormValidators=function(a){a=a.getAttributeNode(\"id\").nodeValue;return this.forms[a]?this.forms[a].validators:[]};nette.validateControl=function(a){var b=this.getFormValidators(a.form)[a.name];return b?b(a):null};nette.validateForm=function(a){var b=a.form||a,d=this.getFormValidators(b);for(var c in d){var e=d[c](a);if(e){b[c].focus&&b[c].focus();alert(e);return false}}return true};nette.toggle=function(a,b){if(a=document.getElementById(a))a.style.display=b?\"\":\"none\"};\/*]]>*\/<\/script>\r\n\r\n<script type=\"text\/javascript\">\r\n\/* <![CDATA[ *\/\r\n\r\nnette.forms = nette.forms || { };\r\n\r\nnette.forms[\"frm-editProductForm\"] = {\r\n\tvalidators: {\r\n\t\t\"name\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tval = nette.getValue(form[\"name\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit n\\u00e1zev produktu.\";\n\t\t},\r\n\t\t\"specs\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tval = nette.getValue(form[\"specs\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit specifikace produktu.\";\n\t\t},\r\n\t\t\"count\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tres = \/^-?[0-9]+$\/.test(val = nette.getValue(form[\"count\"]));\n\t\t\tif (!res) return \"Po\\u010det kus\\u016f mus\\u00ed b\\u00fdt \\u010d\\u00edseln\\u00fd.\";\n\t\t},\r\n\t\t\"url\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tres = \/^.+\\.[a-z]{2,6}(\\\/.*)?$\/i.test(val = nette.getValue(form[\"url\"]));\n\t\t\tif (!res) return \"\\u0160patn\\u00fd form\\u00e1t URL.\";\n\t\t\tval = nette.getValue(form[\"url\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit URL s detaily produktu.\";\n\t\t},\r\n\t\t\"price\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tval = nette.getValue(form[\"price\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit cenu.\";\n\t\t\tres = \/^-?[0-9]+$\/.test(val = nette.getValue(form[\"price\"]));\n\t\t\tif (!res) return \"Cena mus\\u00ed b\\u00fdt \\u010d\\u00edseln\\u00e1.\";\n\t\t}\r\n\t},\r\n\r\n\ttoggle: function(sender) {\r\n\t\tvar visible, res, form = sender.form || sender;\r\n\t}\r\n}\r\n\r\n\r\n\r\n\/* ]]> *\/\r\n<\/script>\r\n\r\n<!-- \/Nette Form validator -->\r\n\n<\/table><\/div>\n"}}

Dovolím si vytáhnout tady tento kousek, tohle je onen formulář, takže v JSONu se vrátí zprávně obsah jaký požaduji, ale nevykreslí se :(

<form action=\"\/mailbox\/shop-for-me\/step-three\/?do=editProductForm-submit\" method=\"post\" id=\"frm-editProductForm\" class=\"ajax\" onsubmit=\"return nette.validateForm(this)\">
<tr><td><label class=\"required\" for=\"frmeditProductForm-name\">N\u00e1zev produktu<\/label><br \/>
<input type=\"text\" class=\"text\" name=\"name\" id=\"frmeditProductForm-name\" value=\"sadwds\" \/><\/td>
<td><label class=\"required\" for=\"frmeditProductForm-specs\">Specifikace produktu<\/label><br \/>
<input type=\"text\" class=\"text\" name=\"specs\" id=\"frmeditProductForm-specs\" value=\"akwdoakd\" \/><\/td>
<td><label for=\"frmeditProductForm-count\">Po\u010det (ks)<\/label><br \/>
<input type=\"text\" class=\"text\" name=\"count\" id=\"frmeditProductForm-count\" value=\"1\" \/><\/td><\/tr>\n
<tr><td><label class=\"required\" for=\"frmeditProductForm-url\">Internetov\u00e1 adresa detailu produktu (URL)<\/label><br \/>
<input type=\"text\" class=\"text\" name=\"url\" id=\"frmeditProductForm-url\" value=\"dawdw.ht\" \/><\/td>
<td><label class=\"required\" for=\"frmeditProductForm-price\">Cena za kus ($)<\/label><br \/>
<input type=\"text\" class=\"text\" name=\"price\" id=\"frmeditProductForm-price\" value=\"150\" \/><\/td>
<td><input type=\"submit\" class=\"button\" name=\"odesli\" id=\"frmeditProductForm-odesli\" value=\"Zm\u011bnit\" \/><\/td><\/tr>\n
<\/form>

pokud stránku teď obnovím, tak se to zobrazí správně

redhead
Člen | 1313
+
0
-

Tak je chyba někde na straně JS, při handlování odezvy. Se podívej do FireBugu jesli něco nehlásí, případně si JS debugni..

pako3
Člen | 18
+
0
-

No já právě ať krokuju jak krokuju, nic špatného v tom nemůžu najít.
Ještě poznatek: při submitnutí toho formuláře se snippet obnoví jak má a vypíše se změněná tabulka…
Díky

Honza Kuchař
Člen | 1662
+
0
-

Nekrokuj, podívej se, co ti posílá server. ;-)

pako3
Člen | 18
+
0
-

No server vrací to co má, aspoň myslím.
Tohle je odezva a vidím tam správně ten formulář:

{"state":[],"snippets":{"snippet--productTable":"<div><table>\n<tr><td>N\u00e1zev produktu<\/td><td>Specifikace produktu<\/td><td>Internetov\u00e1 adresa detailu zbo\u017e\u00ed (URL)<\/td><td>Po\u010det (ks)<\/td><td>Cena za kus ($)<\/td><td>Cena celkem ($)<\/td><\/tr>\n<form action=\"\/mailbox\/shop-for-me\/step-three\/?do=editProductForm-submit\" method=\"post\" id=\"frm-editProductForm\" class=\"ajax\" onsubmit=\"return nette.validateForm(this)\"><tr><td><label class=\"required\" for=\"frmeditProductForm-name\">N\u00e1zev produktu<\/label><br \/><input type=\"text\" class=\"text\" name=\"name\" id=\"frmeditProductForm-name\" value=\"sadwds\" \/><\/td><td><label class=\"required\" for=\"frmeditProductForm-specs\">Specifikace produktu<\/label><br \/><input type=\"text\" class=\"text\" name=\"specs\" id=\"frmeditProductForm-specs\" value=\"Specifikace\" \/><\/td><td><label for=\"frmeditProductForm-count\">Po\u010det (ks)<\/label><br \/><input type=\"text\" class=\"text\" name=\"count\" id=\"frmeditProductForm-count\" value=\"1\" \/><\/td><\/tr>\n<tr><td><label class=\"required\" for=\"frmeditProductForm-url\">Internetov\u00e1 adresa detailu produktu (URL)<\/label><br \/><input type=\"text\" class=\"text\" name=\"url\" id=\"frmeditProductForm-url\" value=\"srgdsrgv.th\" \/><\/td><td><label class=\"required\" for=\"frmeditProductForm-price\">Cena za kus ($)<\/label><br \/><input type=\"text\" class=\"text\" name=\"price\" id=\"frmeditProductForm-price\" value=\"150\" \/><\/td><td><input type=\"submit\" class=\"button\" name=\"odesli\" id=\"frmeditProductForm-odesli\" value=\"Zm\u011bnit\" \/><\/td><\/tr>\n<\/form>\n<!-- Nette Form validator -->\r\n\r\n<script type=\"text\/javascript\">\/*<![CDATA[*\/var nette=nette||{};nette.getValue=function(a){if(a){if(!a.nodeName){for(var b=0,d=a.length;b<d;b++)if(a[b].checked)return a[b].value;return null}if(a.nodeName.toLowerCase()===\"select\"){b=a.selectedIndex;var c=a.options;if(b<0)return null;else if(a.type===\"select-one\")return c[b].value;b=0;a=[];for(d=c.length;b<d;b++)c[b].selected&&a.push(c[b].value);return a}if(a.type===\"checkbox\")return a.checked;return a.value.replace(\/^\\s+|\\s+$\/g,\"\")}};\nnette.getFormValidators=function(a){a=a.getAttributeNode(\"id\").nodeValue;return this.forms[a]?this.forms[a].validators:[]};nette.validateControl=function(a){var b=this.getFormValidators(a.form)[a.name];return b?b(a):null};nette.validateForm=function(a){var b=a.form||a,d=this.getFormValidators(b);for(var c in d){var e=d[c](a);if(e){b[c].focus&&b[c].focus();alert(e);return false}}return true};nette.toggle=function(a,b){if(a=document.getElementById(a))a.style.display=b?\"\":\"none\"};\/*]]>*\/<\/script>\r\n\r\n<script type=\"text\/javascript\">\r\n\/* <![CDATA[ *\/\r\n\r\nnette.forms = nette.forms || { };\r\n\r\nnette.forms[\"frm-editProductForm\"] = {\r\n\tvalidators: {\r\n\t\t\"name\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tval = nette.getValue(form[\"name\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit n\\u00e1zev produktu.\";\n\t\t},\r\n\t\t\"specs\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tval = nette.getValue(form[\"specs\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit specifikace produktu.\";\n\t\t},\r\n\t\t\"count\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tres = \/^-?[0-9]+$\/.test(val = nette.getValue(form[\"count\"]));\n\t\t\tif (!res) return \"Po\\u010det kus\\u016f mus\\u00ed b\\u00fdt \\u010d\\u00edseln\\u00fd.\";\n\t\t},\r\n\t\t\"url\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tres = \/^.+\\.[a-z]{2,6}(\\\/.*)?$\/i.test(val = nette.getValue(form[\"url\"]));\n\t\t\tif (!res) return \"\\u0160patn\\u00fd form\\u00e1t URL.\";\n\t\t\tval = nette.getValue(form[\"url\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit URL s detaily produktu.\";\n\t\t},\r\n\t\t\"price\": function(sender) {\r\n\t\t\tvar res, val, form = sender.form || sender;\r\n\t\t\tval = nette.getValue(form[\"price\"]); res = val!='' && val!=\"\";\n\t\t\tif (!res) return \"Mus\\u00edte vyplnit cenu.\";\n\t\t\tres = \/^-?[0-9]+$\/.test(val = nette.getValue(form[\"price\"]));\n\t\t\tif (!res) return \"Cena mus\\u00ed b\\u00fdt \\u010d\\u00edseln\\u00e1.\";\n\t\t}\r\n\t},\r\n\r\n\ttoggle: function(sender) {\r\n\t\tvar visible, res, form = sender.form || sender;\r\n\t}\r\n}\r\n\r\n\r\n\r\n\/* ]]> *\/\r\n<\/script>\r\n\r\n<!-- \/Nette Form validator -->\r\n\n<\/table><\/div>\n"}}
redhead
Člen | 1313
+
0
-

pako3 napsal(a):

No já právě ať krokuju jak krokuju, nic špatného v tom nemůžu najít.

No tak asi něco špatného tam bude, když to vrací správný výsledek. Odkrokuj su, jesli se to vůbec dostane do nette callbacku a jesli se vykoná vložení toho snippetu do DOMu.

pako3
Člen | 18
+
0
-

Pánové já vás tady bavím kktinou… všem se vám omlouvám a jdu si nafackovat do kouta… chyba byla v šabloně, ale divím se, že se to vůbec nějak vykreslilo. Kdyby ne, tak mi to dojde hned…
Děkuji všem za ochotu a nezlobte se. :)