Form ERROR pri pouziti AJAX + SNIPPET

balicekt
Člen | 52
+
0
-

Zdarvim, mam dotaz ohledne vykreslovani chyb ve formulari pri pouzivani AJAXU.

Pokud udelam

<?php
$form->addError("moje chyba");
?>

Musim dat snippet kolem celeho formulare v sablone tedy:

<?php
{snippet mujerrornippet}
	{form mujform}

	..inputy..

	<div class="error-message" n:if="$form->hasErrors()">
    	<div class="alert alert-danger" role="alert" n:foreach="$form->errors as $error">{$error}</div>
	</div>

	{/form}
{/snippet}
?>

Pokud to mam takto tedy obalen komplet cely formular snippetem vse funguje OK. ALe tim padem se zmeni DOM celeho formulare a prestanou fungovat jQuery naseptavace ktere mam navesene na inputy. Kdyz tedy dam snippet jen kolem error hlasky v sablone viz nize. Tak mi to hlasi undefined variable $form.

<?php
{form mujform}

	..inputy..

	{snippet mujerrornippet}
		<div class="error-message" n:if="$form->hasErrors()">
		    <div class="alert alert-danger" role="alert" n:foreach="$form->errors as $error">{$error}</div>
		</div>
	{/snippet}
{/form}

?>

Existuje tedy neajke jine reseni jak tohle udelat. Nebo spise proc se nemuzou vypsat pouze chyby a musi se invalidovat komplet cely formular a tim tedy zmenit DOM celeho formulare?

Diky za info

Editoval balicekt (22. 4. 2018 22:21)

Šaman
Člen | 2635
+
0
-

Použij makro {snippetArea} okolo celého formuláře.

Zdeno1981
Člen | 114
+
0
-

Ahoj @balicekt,

zkus přes render poslat ten parametr ‚form‘ do šablony

<?php
		$this->template->form = $this['factory']; // Sem zadej svůj název továrničky
?>

takhle to řeším já, když mám obalené snipetem message errory u formulářů.

Editoval Zdeno1981 (23. 4. 2018 8:50)

balicekt
Člen | 52
+
0
-

Šaman napsal(a):

Použij makro {snippetArea} okolo celého formuláře.

Diky za zpravu, ale pouziti snippetArea nic nemeni i kdyz pridam snippetArea kolem celeho formulare dostanu chybu pri renderenovani stranky „undefined variable $form“ tobe to funguje se snippetArea?

balicekt
Člen | 52
+
0
-

Zdeno1981 napsal(a):

Ahoj @balicekt,

zkus přes render poslat ten parametr ‚form‘ do šablony

<?php
		$this->template->form = $this['factory']; // Sem zadej svůj název továrničky
?>

takhle to řeším já, když mám obalené snipetem message errory u formulářů.

Diky tohle funguje, sice se mi to reseni moc nelibi, ale lepsi nez vyuzivat misto toho flashmessages :)

balicekt
Člen | 52
+
0
-

Nasel jsem jeste jeden workaround a to misto $form vyuzivat v sablone: $this->global->uiControl[„factoryName“]. Opravdu zadne jine reseni bez tohoto obchazeni neni? Jak invalidovat pouze errory formu a ne cely formular? @DavidMatějka ? Diky za info.

Šaman
Člen | 2635
+
+1
-

balicekt napsal(a):

… tobe to funguje se snippetArea?

Funguje. Používám to na závislé inputy. Nette 2.4. Nejsou už potřeba hacky s předáváním $_control a $_form apod.
Je potřeba invalidovat celou textareu a pak teprve snippet, který chceš překreslit. (Textarea slouží k tomu, abys označil další kód, který je nutný k úspěšnému překreslení snippetu. Takže potřebuješ $form, abys mohl načíst jeho chyby. Ale vykreslovat budeš jen ty chyby.)
Dále je nutné mít obsluhu úspěšného i chybového odeslání a v ní ty chyby překreslit.

Posílám ukázku se závislými inputy. U tebe to bude jednodušší, nepotřebuješ ten JS a handleInvalidateTestForm. Ale jinak to bude podobné.

<?php

namespace App\Presenters;
use Nette\Application\UI\Form;


class DependentInputsPresenter extends BasePresenter
{

	var $countries = ['cz' => 'Česká Republika', 'sk' => 'Slovenská republika'];
	var $cities = [
				'cz' => ['jbc' => 'Jablonec nad Nisou', 'lbc' => 'Liberec', 'pha' => 'Praha'],
				'sk' => ['ko' => 'Košice', 'po' => 'Poprad']
			];

	protected function createComponentTestForm($name)
	{
		$form = new Form($this, $name);
		$form->getElementPrototype()->addAttributes(['class' => 'ajax', 'novalidate' => 'novalidate']);

		$form->addSelect('country', 'Země:', $this->countries)
				->setAttribute('data-link', $this->link("invalidateTestForm!"))
				->setPrompt('- vyberte zemi -')
				->setRequired('vyberte zemi');

		$cities = $form['country']->value ? $this->cities[ $form['country']->value ] : [];
		$form->addSelect('city', 'Město:', $cities)
				->setPrompt('- vyberte město -')
				->setRequired('vyberte město');

		$form->addSubmit('ok', 'Odeslat');

		$form->onSuccess[] = [$this, 'testFormSuccess'];
		$form->onError[] = [$this, 'testFormError'];

		return $form;
	}

	public function testFormSuccess(Form $form, $values)
	{
		if ($this->isAjax()) {
			$this->flashMessage('Data odeslána: ' . implode(', ', array_values($form->getValues(TRUE))) . '.');
//			bdump('onSuccess ajax');
			$this->redrawControl('flashMessages');
			$this->redrawControl();
		}
		else {
			$this->flashMessage('Data odeslána: ' . implode(', ', array_values($form->getValues(TRUE))) . '.');
//			bdump('onSuccess no-ajax');
			$this->redirect('this');
		}
	}

	public function testFormError(Form $form)
	{
		if ($this->isAjax()) {
			$this->flashMessage('Data nebyla odeslána: ' . implode(', ', array_values($form->getValues(TRUE))) . '.');
//			bdump('onError ajax');
			$this->redrawControl('flashMessages');
			$this->redrawControl('testFormArea');
			$this->redrawControl('form-errors');
		}
		else {
			$this->flashMessage('Data nebyla odeslána: ' . implode(', ', array_values($form->getValues(TRUE))) . '.');
//			bdump('onError no-ajax');
			$this->redirect('this');
		}
	}

	public function handleInvalidateTestForm($country)
	{
		if( in_array($country, array_keys($this->countries)) ) {
			$this['testForm']['city']->setItems($this->cities[$country]);
		}
		else {
			$this['testForm']['city']->setItems([]); # uživatel vybere prompt (nulovou) hodnotu
		}
		$this->redrawControl('testFormArea');
		$this->redrawControl('inputCity');
	}

}
?>
{block title}Závislé inputy{/block}


{block content}
	{snippetArea testFormArea}
	{form testForm}
		{snippet form-errors}
			{var $form = $control['testForm']}
			<ul class="errors" n:if="$form->hasErrors()">
				<li n:foreach="$form->errors as $error">{$error}</li>
			</ul>
		{/snippet}

		<table>
		<tr>
			<th>{label country}</th>
			<td>{input country}</td>
		</tr>

		<tr>
			<th>{label city}</th>
			<td>{snippet inputCity}{input city}{/snippet}</td>
		</tr>

		<tr>
			<th></th>
			<td>{input ok}</td>
		</tr>
		</table>
	{/form}
	{/snippetArea}
{/block content}


{block scripts}
<script>
$(function () {

	var link = $('#frm-testForm-country').data('link');

	$('#frm-testForm-country').on('change', function () {
		$.nette.ajax({
			url: link,
			data: {
				'country': $('#frm-testForm-country').val()
			}
		});
	});

});
</script>
{/block scripts}

Editoval Šaman (23. 4. 2018 12:05)

balicekt
Člen | 52
+
+1
-

SUPER moc dekuji za pomoc. Cely trik tedy spociva v tomto radku

<?php
{var $form = $control['testForm']}
?>

proto mi to psalo undefined variable $form. A dokonce to funguje i bez snipettArea invaliduji pouze ten kod s chybou.

Jeste jednou moc dekuji za pomoc!!!

Šaman
Člen | 2635
+
0
-

Aha, tak na to už jsem zapomněl. Myslel jsem, že tyhle hacky už nejsou potřeba, ale když to tam mám, tak asi jo. Hrál jsem si s tím docela dlouho, abych nemusel předávat nic do šablony a zároveň aby mi fungovaly flashmessages, errory a závislé inputy.
Tak aspoň že sis to tam našel :)

Zdeno1981
Člen | 114
+
0
-

ono ve výsledku to vyjde stejně, jestli to nastavíš přes mackro v latte

	{var $form = $control['factory']}

nebo to pošleš do latte

	$this->template->form = $this['factory'];

ve výsledku tu snipetAreu nepotřebuješ.

Šaman
Člen | 2635
+
0
-

Není to jedno, pokud máš víc formulářů.
Ano, snippetArea v tomto případě není nutná (i když teoreticky by měla stačit ta snippetArea a ne tenhle hack). Když si to ale načteš v šabloně, tak:

  • Nezatěžuješ presenter tím, že tohle ještě snippetArea neumí. Až to bude umět, stačí upravit šablonu.
  • Ale hlavně v případě více formulářů si v té šabloně načteš do lokální proměnné $form vždy ten aktuální formulář (není úplně čisté ho volat jménem, ale vzhledem k tomu, že to samé jméno se používá i dva řádky nad tím, tak to snad tak nevadí).