Zobrazení/skrytí více formulářů pomocí Ajaxu

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

Mám dotaz ohledně zobrazování a následného schovávání formulářů po kliknutí na patřičný odkaz (tlačítko). Tlačítka a formuláře jsou v komponentě asi takto:

sablona.latte

<p>
	<a n:href="showForm! form1" class="ajax">Formulář 1</a>
	<a n:href="showForm! form2" class="ajax">Formulář 2</a>
	<a n:href="showForm! form3" class="ajax">Formulář 3</a>
</p>
{snippet form}
	{if $formShowed == 'form1'}
		{control form1}
	{elseif $formShowed == 'form2'}
		{control form2}
	{elseif $formShowed == 'form3'}
		{control form3}
	{/if}
{/snippet}

Výňatek z komponenty asi takto:

control.php

public $formShowed = '';

public function render(){
	$this->template->formShowed = $this->formShowed;
	$this->template->setFile(__DIR__ . '/sablona.latte');
	$this->template->render();
}

public function handleShowForm($id){
	if($this->formShowed == $id) $this->formShowed = '';
	else $this->formShowed = $id;

	if($this->presenter->isAjax()){
	    $this->redrawControl('form');
	}
}

Názvy formulářů jsou vymyšleny, ve sktutečnosti mají reálné názvy a každý formulář má jinou funkci.

Idea je taková, abych kliknul na tlačítko Formulář 1 a zobrazil se mi daný formulář form1. Kliknu na tlačítko Formulář 2 a zobrazí form2 (form1 zmizí). A ono to i funguje.
Ale ještě bych rád, aby když kliknu podruhé na tlačítko Formulář 2 (po prvním kliknutí se formulář zobrazil), tak aby tento formulář zmizel a snippet zůstal prázdný. Očekával jsem, že toho docílím tímto kódem:

	if($this->formShowed == $id) $this->formShowed = '';
	else $this->formShowed = $id;

ale nestalo se tak. V metodě handleShowForm() proměnná $this->formShowed zřejmě obsahuje hodnotu z url odkazu tlačítka ještě před přiřazením. Podmínka if($this->formShowed == $id) tedy není splněna. Očekával jsem, že v této proměnné bude uložena hodnota z minulého volání.

Jistě to bude nějaká prkotina, budu rád za každou pomoc či nakopnutí či prozrazení jiného fíglu, jak docílit požadované.

xrep
Člen | 51
+
+1
-

mozno by stalo za uvazenie .hide() (jQuery) ?

inak formShowed property bude pri kazdom requeste „null“. Aby to nebolo null, tak by si ten parameter mal mat @persistent (to sa mi ale nejako nepodarilo rozbehat v tomto pripade).
Dalsia moznost je ulozit si posledny zobrazeny form do cookie napr. takto:

<?php
public function handleShowForm( $id ){

        if( $this->isAjax() ){
            $lastForm = $_COOKIE[ 'lastForm' ];
            if ( $lastForm == $id ) {
                $this->template->formShowed = null;
                setcookie('lastForm', 'empty');
            }
            else {
                setcookie('lastForm', $id );
                $this->template->formShowed = $id;

            }
            $this->redrawControl('form');
        }
    }
?>

to ti fungovat bude.

Ale to moje riesenie ber s rezervou, neviem nakolko je to „idealne“ a niekto ti pravdepodobne poradi lepsie, ja som noob :)

Unlink
Člen | 298
+
0
-

Tak môžeš si aj tie linky odpodmienkovať

<a n:if="$formShowed!='form1'" n:href="showForm! form1" class="ajax">Formulář 1</a>
<a n:if="$formShowed!='form2'" n:href="showForm! form2" class="ajax">Formulář 2</a>
<a n:if="$formShowed!='form3'" n:href="showForm! form3" class="ajax">Formulář 3</a>
<a n:if="$formShowed!=''" n:href="hideForm! " class="ajax">Schovaj formulář</a>

a potom pridať handle na hideForm

Editoval Unlink (14. 9. 2015 21:05)

flamengo
Člen | 135
+
0
-

jQuery
Pomocí jQuery by to určitě šlo, ale vzhledem k účelu aplikace to není vhodné řešení. Na stránce bude cca 20 příspěvků a tyto formuláře budou pod každým z nich a bude jich 5: 3 formuláře a 2 tabulky obsahující až několik desítek řádků. Kdybych načetl vše a poté schoval pomocí jQuery, byl by objem přenesených dat do prohlížeče několikanásobně vyšší a já bych rád, aby to hlavně na mobilech bylo svižný.

Persistent
Změna parametru na persistent nepomohla. Zobrazování formulářů přestalo fungovat úplně.

/** @persistent string */
public $formShowed = '';

Podmínkování linků
Tato možnost mě napadla, ale abych se přiznal, moc se mi to nelíbilo. Asi kvůli potřebě psát další handler a komplikovat trochu šablonu.

$_COOKIE
Použití $_COOKIE mě vůbec nanapadlo, zkusil jsem a funguje pěkně. Kód jsem trochu předělal, protože jak jsem uvedl výše, jedná se formuláře pro více položek na jedné stránce.

public function handleShowForm($formShowed){
	$id = $this->itemId;
	if(isset($_COOKIE['lastForm'][$id]) && $_COOKIE['lastForm'][$id] == $formShowed){
		$formShowed = NULL;
	}
	setcookie('lastForm['.$id.']', $formShowed);
	$this->formShowed = $formShowed;

	if($this->presenter->isAjax()){
	    $this->redrawControl('form');
	}
	else{
		$this->presenter->redirect('this');
	}
}

Do konstruktoru jsme musel přidat unset($_COOKIE['lastForm']);, protože když jsem např. u první položy kiknul na Formulář 1, tak se zobrazil a pokdu jsem potom proved reload stránky a znova u té samé položky kliknul na Formulář 1, tak proměnná již obsahovala hodnotu a vlastně formulář chtěla schovat a nic se nestalo.

No a nakonec jsem si to předělal do SESSIONS, což mi přijde vhodnější vzhledem k požadavkům na funkci.

public function handleShowForm($formShowed){
	if($this->sessionSection->formShowed == $formShowed) $formShowed = NULL;
	$this->sessionSection->formShowed = $formShowed;
	$this->formShowed = $formShowed;

	if($this->presenter->isAjax()){
	    $this->redrawControl('form');
	}
	else{
		$this->presenter->redirect('this');
	}
}

V konstruktoru jsme vynuloval proměnnou $this->sessionSection->formShowed = NULL; (stejný problém při reloadu stránky jako s COOKIE).

Díky všem za názory!

flamengo
Člen | 135
+
0
-

Malá oprava. Po přidání $this->sessionSection->formShowed = NULL; do konstruktoru přestalo fungovat schovávání. Zkoušel jsem to předtím a měl jsem za to, že to fungovalo, nevím.
Místo toho jsem tam dal $this->formShowed = $this->sessionSection->formShowed;. Po reloadu se tedy zobrazí předtím otevřený formulář, takže přípustné.

Unlink
Člen | 298
+
+1
-

No konštruktor sa volá vždy, takže aj pri tom ajaxovom požiadavku. Ak to chceš vynulovať tak v príslušnej render metóde to nejako rozumne odpodmienkovať.

Len ak takto používaš session, tak to začne robiť srandovné veci ak si to otvoríš v dvoch oknach naraz.

Ja by som tie podmienky nezavrhoval, a čo takto?

<a n:href="showForm! $showForm == 'form1' ? '' : 'form1'" class="ajax">Form 1</a>
<a n:href="showForm! $showForm == 'form2' ? '' : 'form2'" class="ajax">Form 2</a>
<a n:href="showForm! $showForm == 'form3' ? '' : 'form3'" class="ajax">Form 3</a>
public function handleShowForm($form) {
    $this->showForm = $form;
    if ($this->isAjax()) {
		$this->redrawControl('form');
	}
}

Editoval Unlink (15. 9. 2015 17:26)

flamengo
Člen | 135
+
0
-

Nevěděl jsem, co se přesně děje při Ajaxovým požadavku a to, že se konstruktor volá také mi už došlo. Škoda, že jsem to nevěděl dřív, ušetřil bych si nervy :) Každopádně díky za potvrzení této mé domněnky.

Hm ve více oknech při použití Session to nefunguje jak má, to je jasné.

Dal jsem tedy na tvou radu a funguje to vlastně přesně tak, jak jsem chtěl hned na začátku, takže velké díky.

Ale jeden menší zádrhel se opět vyskytnul. Pokud zobrazím Formulář 1 a pomocí Ajaxu ho odešlu a poté bych ho chtěl překreslit a vynulovat hodnoty a zobrazit také flash zprávu, tak šablona vlastně neví, jaký formulář zobrazit.

public function processForm1(Form $form){
	...
	$this->flashMessage('Formulář 1 zpracován', 'alert-info');

	$form->setValues(array(), TRUE);
	if($this->presenter->isAjax()){
	    $this->redrawControl('flashes');
	    $this->redrawControl('form');
	}
	else{
		$this->presenter->redirect('this');
	}
}

Napadá tě jak z toho ven? :)
Přidat do adresy zpracování formuláře nějaký GET parametr nebo tak něco? Jde to vůbec?

Editoval flamengo (15. 9. 2015 19:36)

Unlink
Člen | 298
+
0
-

No veď v tej process metóde vieš ktorý form si spracoval
takže pri ajaxovom požiadavku by malo stačiť
$this->showForm = 'form1';
ale pri tom klasickom možno
$this->presenter->redirect('showForm! form1'); ale niesom si isty či sa dá spraviť redirect aj na signál, ak nie tak pomocou nejakého parametra $this->presenter->redirect('this form=>form1'); plus úprava render metódy

flamengo
Člen | 135
+
0
-

Díky díky! Už jsme na to přišel taky a opravdu stačilo přídat $this->showForm = 'form1'; do metod pro zpracování formulářů (processForm1). Takže snad již hotovo, uvádím rekapitulaci kdyby se s tím někdo někdy pral jako já :D

Komponenta.php

class Komponenta extends \Nette\Application\UI\Control{

	/** @var string */
	public $showForm = '';

	public function render(){
		$this->template->showForm = $this->showForm;
		$this->template->setFile(__DIR__ . '/komponenta.latte');
		$this->template->render();
	}

	public function handleShowForm($showForm){
		$this->showForm = $showForm;
		if($this->presenter->isAjax()){
		    $this->redrawControl('forms');
		}
		else{
			$this->presenter->redirect('this');
		}
	}

	protected function createComponentForm1(){
		$form = new Form();
		$form->getElementPrototype()->class('ajax');
		$form->addText('example', 'Příklad:');
		$form->addSubmit('send', 'Vložit');
		$form->onSuccess[] = array($this, 'processForm1');
		return $form;
	}

	public function processForm1(Form $form){
		// nějaký kód pro zpracování formuláře
		// ....
		$this->flashMessage('Formulář 1 zpracován.', 'alert-info');
		$this->showForm = 'form1';  // tímto docílím zobrazení správného formuláře v šabloně
		if($this->presenter->isAjax()){
		    $this->redrawControl('forms');
		    $form->setValues(array(), TRUE);  // vynulování hodnot
		}
		else{
			$this->presenter->redirect('this');
		}
	}

komponenta.latte

{snippet forms}
	<p>
		<a n:href="showForm! showForm != 'form1' ? 'form1'" class="ajax">Form 1</a>
		<a n:href="showForm! showForm != 'form2' ? 'form2'" class="ajax">Form 2</a>
		<a n:href="showForm! showForm != 'form3' ? 'form3'" class="ajax">Form 3</a>
	</p>

	<div n:foreach="$flashes as $flash" n:class="$flash->type">
		{$flash->message}
	</div>

    {if $showForm == 'form1'}
        {control form1}
    {elseif $showForm == 'form2'}
        {control form2}
    {elseif $showForm == 'form3'}
        {control form3}
    {/if}
{/snippet}