Step form – best practice
- MartinitCZ
- Člen | 580
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
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
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
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
D9ky už to mám. Upsal jsem se, což byl celý problém. Díky za jednoduché nastínění :)
- elendir
- Člen | 31
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
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();
}
- 22
- Člen | 1478
<?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)
- 22
- Člen | 1478
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]}
- Hafran
- Člen | 121
@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.