Step form – best practice

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

Hledal jsem zde nějaké komponenty, informace … jak řešit krokový formulář, ale nikde nic.

Jak to řešíte?

Potřebuji formulář, který obsahuje jeden input → vloží se do něj URL → zkontroluje zda je URL v pořádku → zobrazí další formulář, který bude naplněn daty zparsované z URL.

Nejlepší by bylo, kdyby to byla jedna komponenty {control stepForm}

Tomáš Jablonický
Člen | 115
+
+1
-

Ahoj. Já osobně to řeším tak, že vytvořím více formulářů ( co krok to formulář ) a data předávám v session – v posledním kroku to uložím do DB.

Více násobný formulář zaobalený v jednom controloru by se řešil poměrně jednoduše. V controloru by jsis vytvořil formulářové componenty ( createComponent<step1>($name), … , createComponent<stepN>($name) ). Protože controler neumí action ani render, tak by se o jednotlivé vykreslení ( asi špatně formulováno ) a logiku starali signály ( handle<step1>($param1, … , $paramN), … , handle<stepN>($param1, … , $paramN) ).

Formuláře v template by jsis pak vykreslil buď ručně nebo přes {control step1}…

<?php

class StepsForm extends Nette\Application\UI\Control
{
   ... //construct atd

  protected function createComponentStep1($name)
  {
     $form = new Nette\Forms\Form();
     .... //dalsi prvky
     $from->addSubmit('submit', 'Další krok')
          ->onClick[] = $this->process1;
     return $form;
  }

  ... //dlsi formulare

  public fuction process1(SubmitButton $btn)
  {
     //zpracujeme formular a ulozime do session

     $this->redirect('step2'); //nejsem si jist jestli to takto na signal jde
  }

  public function handleStep2()
  {
	$this->template->step2 = true; //zde předám do šablony, že se má zobrazit druhý krok - samozřejmě to jde i jinak :-) než přes true
  }
  ... obdobně pokračuji do posledního kroku


}
?>

V šaloně componenty pak:

<?php

 {if $step1}{control step1}{/if}
  ....
 {if $stepN}{control stepN}{/if}

atd
?>

A v samotné šabloně presenteru.

{control stepsForm}

Psal jsem to rychle ale je to asi to co potřebuješ.

A nebo použíj z addons https://componette.org/search/?….

MartinitCZ
Člen | 580
+
0
-

Ve zpracování prvního formu si data uložim do session.
V druhý form jimi chci naplnit, ale sesion je prázdná :/

Co s tím?

Tomáš Jablonický
Člen | 115
+
0
-

martinit napsal(a):

Ve zpracování prvního formu si data uložim do session.
V druhý form jimi chci naplnit, ale sesion je prázdná :/

Co s tím?

Ukaž kód, jak ukládáš session.

MartinitCZ
Člen | 580
+
0
-

D9ky už to mám. Upsal jsem se, což byl celý problém. Díky za jednoduché nastínění :)

elendir
Člen | 31
+
0
-

Osobně to řeším obdobně, ale komponenty zanořuji do sebe. Takže komponenta Step1 obsahuje komponentu Step2, ta zase Step3 atd. Všechny dědí od předka WizardControl, který rovněž definuje pořadí kroků. Uživateli se pak kroky ajaxově loadují pod sebe a díky zmiňovanému zanoření se to krásně jednoduše invaliduje.

22
Člen | 1478
+
+1
-

Osobně mám tedy udělanou komponentu StepForm, do ktere si nasypu formuláře, které mají být součástí krokového formuláře. Komponenta se pak postara o jejich vyrendrování vč. menu a kontrolu pohybu ve formuláři.

V presenteru to vypadá:

protected function createComponentStepForm()
{
	$stepForm = new StepForm();
	$stepForm->addStep('Krok 1', $this->getComponent('step1'));
	$stepForm->addStep('Krok 2', $this->getComponent('step2'));

	return $stepForm;
}

protected function createComponentStep1()
{
	return new Form();
}

protected function createComponentStep2()
{
	return new Form();
}
tatyalien
Člen | 239
+
0
-

22: Tohle řešení se mě zamlouvá, nehodil by jsi i nástřel komponenty?

22
Člen | 1478
+
0
-
<?php

namespace Addons;

use Nette\Application\UI\Control;
use Nette\Application\UI\IRenderable;
use Nette\Application\UI\Presenter;

class StepForm extends Control
{

	private $steps;

	private $menu;

	private $position;


	public function addStep($linkName, IRenderable $componentName)
	{
		$counter = count($this->menu) + 1;
		$this->menu[$counter] = $linkName;
		$this->steps[$counter] = $componentName;
	}


	protected function attached($obj)
	{
		parent::attached($obj);

		if ($obj instanceOf Presenter) {
			$this->position = $this->getPresenter()->getParameter('step') ? (int) $this->getPresenter()->getParameter('step') : 1;
			foreach ($this->steps as $key => $step) {
				$steps[$key] = $step;
			}

			$this->steps = $steps;
		}
	}


	public function render()
	{
		$template = $this->getTemplate();
		$template->setFile(__DIR__ . '/stepForm.latte');

		$template->steps = $this->steps;
		$template->menu = $this->menu;
		$template->position = $this->position;

		$template->render();
	}

}

Editoval 22 (3. 10. 2012 22:56)

Felix
Nette Core | 1245
+
0
-

To vypada podobne hezky jako treba Multiplier. Jednoducha komponenta, ktera ve vysledku zvladne vse.

22
Člen | 1478
+
0
-

Není to úplně vypilované, je to jen draft, ale funguje, narychlo jsem to někde potřeboval. Ještě šablona případně:

<ul class="nav nav-tabs">
{foreach $menu as $key => $step}
	<li n:class="$key === $position ? active"><a href="{plink this step => $key}">{$step}</a></li>
{/foreach}
</ul>

{control $steps[$position]}
tatyalien
Člen | 239
+
0
-

Náhodou pěkně řešené ;) to já to vždy piplal v šabloně kde jsem měl něco jako:

{if $position == 1}
...
{elseif $position == 2}
...
{/if}
Tomáš Jablonický
Člen | 115
+
0
-

@22 … pěkné, hned si to ukradnu :-). Nechceš z toho udělat addons?

MartinitCZ
Člen | 580
+
0
-

@jablon: Addon asi né, ale jako tutorial do planette by to bylo skvělé.

Hafran
Člen | 121
+
0
-

@22 Tohle řešení vypadá moc pěkně, dík, užiju to.

Moh by mě někdo ještě popostrčit jak automaticky komponentou přepsat submity těch jednotlivých formulářů a ty pak zavolat najednou?
Tedy, že by každý z těch dílčích formulářů měl normální callback na onSubmit, ale stepForm by je plošně nahradil tlačítkem Next, které by jen validovalo, ale nepouštělo ten callback (abych mohl použít už hotový formulář – už tak jich mám mraky a že bych dělal ještě dvě verze pro stepForm a pro samostatné použití mi příjde k zbláznění ;) a aby když projedu všechny kroky až na konec, tak by se zavolaly všechny ty metody onSubmit najednou.