Jak na vlastní komponentu
- kleinpetr
- Člen | 480
Ahoj, snažím se vytvořit vlastní komponentu na krokový formulář. Inspiroval jsem se zde https://forum.nette.org/…est-practice
Nicméně potřebuji trochu jiné použití a jelikož dělám v nette krátce, řekl jsem si že si ji zkusim napsat sám.
Zatím vše vypadá funkčně jen mi nejde do hlavy jak vytvářet odkazy a
jak volat signály.. v komponentě podle které to zkouším, je funkce
attached()
, která jak jsem se dočetl monitoruje změny nad
presenterem. Nicméně odkaz poté v šabloně vypadá takto
{plink this, var=>'neco'}
v dokumentaci je ale příklad na fifteen: https://github.com/…ster/Fifteen
a zde David používá signály. Přijde mi to hezčí, ikdyž mi to generuje
zvláštní url: ?stepForm-step=3&do=stepForm-Step
. Proč
v krokovém formuláři je použito attached()
ale jinak se
používají signály v čem e to lepší nebo rozdílné ?
a dále bych potřeboval zjistit jak se dá formulář odeslat do komponenty, kde bych si například jeho data uložil do session. Díky
Editoval kleinpetr (27. 3. 2015 15:39)
- Jan Suchánek
- Člen | 404
@kleinpetr Udělej si model kterej pracuje se session a ten model používej v komponentě a výsledky formu předávej normálně tomu modelu.
- kleinpetr
- Člen | 480
Asi jsem měl zmínit, že rozděluju na kroky pouze jeden fomrulář který je rozdělen na groupy a každá group je jeden krok.. tudíž jeden krok je pouze sada několika inputů a potřenuji vždy tu sadu za pomoci nějakého buttonu odeslat do nějakého signálu nebo tak někam, kde to potom zpracuji.
- Vojtěch Dobeš
- Gold Partner | 1316
kleinpetr: Opravdu by mi někdo nevysvětlil jaký je rozdíl mezi použitím attached() nebo signálu ?
Neřekl bych, že metoda attached
monitoruje změny nad
presenterem. Kde jsi se to dočetl?
Každopádně jak je to přesně: metoda attached
je zavolána,
jakmile je komponenta připojena k monitorovanému rodiči. Potomci
UI\Control
by default monitorují presenter, proto je metoda
attached
ve všech potomcích UI\Control
(což jsou
klasické vykreslované komponenty) zavolána, jakmile je tato komponenta
připojena k presenteru. Metoda se hodí, pokud potřebuješ mít jistotu, že
se některý kód vykoná teprve, jakmile je komponenta připojena k presenteru
(například vytvoření odkazu může záviset na presenteru apod.).
Signály jsou docela jiná věc. handle
metody jsou rozhraním
komponenty, vyjadřují, co může uživatel webu s komponentou dělat
(kliknout na odkaz, odeslat formulář… to vše budou signály).
Klidně se ptej dále, pokud je to stále nejasné.
- kleinpetr
- Člen | 480
Moc díky, snažím se totiž vytvoři krokový formulář na úrovni groups.
Mám napsanou komponentu StepForm()
do které pošlu nějaký již
vytvořený form. Tenhle form je rozdělen třeba na 4 groups a já nyní vezmu
ten form, proiteruju ty groupy a každou tu group si uložím jako step pod
klíčem currentStep. Poté mám signál handleStep()
jejímž
parametrem je nový currentStep, tenhle signál prozatím jen změní pozici
currentStep.. a podle aktuální pozice se pak vykreslí daná group. Po
odeslání prvního kroku, kde má submit nastavenou scope pro první kontejner,
se uloží hodnoty do sessionSteps, v metodě saveForm()
kontroluji zda už nejsem na posledním korku a pokud ano tak si vezme všechny
ty sessionSteps a vrátí je do presenteru. Pro ukázku zasílám kód. Budu
rád když mě někdo opraví, navede, prostě cokoliv, abych se poučil. Zdá
se mi to celé takové nepřehledné, kdyby měl někdo tip co by se alo
zkrátit nebo vylepšit, určitě to přivítám :).
- Nepodařilo se mi udělat vícerozměrné pole v session, neco jako
$this->session['steps']['step1'];
končí errorem, proto jsem vytvořil isessionSection
pro steps zvlášť. - Ještě bych se chtěl zeptat jak mohu manuálně spustit validaci nad
celým formulářem ? Například něco jako
$form->isValid();
? díky.
<?php
namespace App\Components;
use Nette\Application\UI\Control;
use Nette\Application\UI\Presenter;
use App\Model\FormModelParser;
use Nette\Http\Session;
class StepForm extends Control
{
private $steps;
private $menu;
private $form;
public $onFinish;
private $session;
private $sessionSteps;
public function __construct($uniqueName = null, FormModelParser $form, Session $session)
{
parent::__construct();
$this->session = $session->getSection($uniqueName);
$this->sessionSteps = $session->getSection($uniqueName . '_steps');
if (!isset($this->session->currentStep)) {
$this->session->currentStep = 1;
}
$this->form = $form;
$this->setSteps();
}
private function setSteps()
{
$form = $this->form;
foreach ($form->groups as $group) {
$counter = count($this->menu) + 1;
$this->menu[$counter] = $group->getOption('label');
$this->steps[$counter] = $group;
}
}
public function handleStep($position)
{
if (!$this->isRange($position)) {
$position = 1;
$this->flashMessage('Neplatný krok', 'danger');
}
$this->session->currentStep = $position;
$this->redrawControl();
}
protected function createComponentForm()
{
$form = $this->form;
$form->onSuccess[] = [$this, 'saveForm'];
return $form;
}
public function saveForm($form)
{
$session = $this->session;
$sessionSteps = $this->sessionSteps;
$vals = $form->values;
$stepValues = [];
$sessionSteps[$session->currentStep] = $vals['step_' . $session->currentStep];
$this->flashMessage('Step ' . $session->currentStep . ' save');
if ($session->currentStep == count($this->steps)) {
foreach ($sessionSteps as $step) {
foreach ($step as $key => $val) {
$stepValues[$key] = $val;
}
}
//zde jeste zvaliduji cely formular najednou.
$this->onFinish($stepValues);
$this->flashMessage('Form send to Presenter', 'success');
}
if ($this->isRange(($session->currentStep+1))) {
$this->session->currentStep = $session->currentStep+1;
$this->redrawControl();
}
}
public function render()
{
$template = $this->template;
$template->setFile(__DIR__ . '/StepForm.latte');
$template->steps = $this->steps;
$template->menu = $this->menu;
$template->currentStep = $this->session->currentStep;
$template->render();
}
protected function isRange($position)
{
if ($position <= count($this->menu)) {
if ($position > 0) {
return true;
}
}
return false;
}
}
?>
v templatě pak mám:
<div class="row">
{snippet flashes}
//flashes..
{/snippet}
</div>
{snippet menu}
<div class="row steps_box">
{foreach $menu as $key=>$step}
<a class="ajax" n:href="Step! $key">
<div class="step_box {if $key<=$currentStep}active{/if}">{$step}</div>
</a>
{/foreach}
</div>
{/snippet}
<div class="row">
{snippet step}
{form form class=>'ajax'}
{var $group = $steps[$currentStep]}
<fieldset>
<legend>{$group->getOption('label')}</legend>
{foreach $group->controls as $item}
<div class="form-group>
{$item->label}
{$item->control}
</div>
{/foreach}
</fieldset>
{/form}
{/snippet}
</div>
a v presenteru tohle:
protected function createComponentStepForm()
{
$form = new Model\FormModelParser();
$stepForm = new StepForm('userRegistration',$form,$this->session);
$stepForm->onFinish[] = [$this,'saveStepForm'];
return $stepForm;
}
public function saveStepForm($values){
dump($values);
//zde už mám všechny data z formuláře ...
}
Je to má první komponenta, takže budu opravdu rád za každou krtiku, nápad či pomoc :) díky všem.
Editoval kleinpetr (29. 3. 2015 18:58)