Skládaní komponent a formuláře
- kedrigern
- Člen | 102
Zdravím,
narazil jsem na problém. Mám v aplikaci docela dost formulářů a některé části se tam docela často opakují (třeba adresa). Tak jsem si říkal, že tyto části formulářů vyhodím zvlášť a budu je přidávat do jednotlivých fomulářů.
Inu udělal jsem v BasePresenteru (zjednodušují):
protected function createComponentAddress() {
$form = new Form()
$form->addText('street', 'Ulice:');
return $form;
}
A v dalších prezenterech jsem chtěl provést něco ve stylu:
$form2->addComponent($this->createComponentAddress() , 'address');
Přičemž sem očekával, že se to bude chovat stejně jako, kdybych udělal:
$sub = $form2->addContainer('address');
$sub->addText('street', 'Ulice:');
(což běžně používám)
Nicméně metoda addComponet vyhodila výjimku:
`Nette\InvalidStateException
Nested forms are forbidden`
A já se ptám co s tím? Lze nějak vkládat již hotové formuláře jako součásti jiných? Ta výjimka hovoří docela jasně, ale nějak se mi nezdá, že by to nešlo.
- kedrigern
- Člen | 102
Jasně! Zde to je: https://doc.nette.org/cs/forms#…
Ale mám ještě jednu otázku.
Lze v presenteru ovlivnit pořadí komponent? Samozřejmě to mohu vypsat manuálně (mám dojem, že i po komponentách, že?), ale mě by stačilo zařadit tu zděděnou část formuláře nakonec.
Popřípadě lze takto sestavit formulář z více prefabrikátů?
- llook
- Člen | 407
Dědičnost formulářů je většinou špatné řešení. Vztah formuláře k adrese je has-a, proto je vhodnější kompozice. Lze to dekomponovat přibližně takto:
use Nette\Forms\Container;
use Nette\Application\UI\Form;
class Address extends Container
{
public function __construct()
{
parent::__construct();
$this->addText('street', 'Ulice');
$this->addText('city', 'Město');
}
}
class NějakýFormulářSAdresou extends Form
{
public function __construct()
{
parent::__construct();
$this['address'] = new Address;
$this->addSubmit('save', 'Uložit');
}
}
- kedrigern
- Člen | 102
Tak jsem zkusil tu dědičnost.
Samostatně mám:
<?php
use Nette\Forms\Container;
use Nette\Application\UI\Form;
class AddressForm extends Container
{
public function __construct()
{
parent::__construct();
$this->addText("street", "Ulice:")
->addRule(Form::MAX_LENGTH, 'Jméno ulice nesmí být delší než %d znaků', 30);
//...
}
}
class RegistrationForm extends Form
{
public function __construct()
{
parent::__construct();
$this->addGroup("Základní údaje");
$this->addText("username", "Uživatelské jméno (nick):");
// ...
$this->addGroup("Adresa");
$this['address'] = new AddressForm;
$this->addGroup("Doplňující údaje");
$this->addText("tel", "Telefon:")
->addRule(Form::MAX_LENGTH, 'Telefon nesmí být delší než %d znaků', 15);
$this->addSubmit("registrovat", "Registrovat")
->setAttribute('class', 'btn btn-large btn-primary');
}
}
V prezenteru mám:
protected function createComponentRegistrationForm()
{
$form = new \RegistrationForm;
$form->onSuccess[] = callback($this, "registrationFormSubmitted");
return $form;
}
A v template:
{control registrationForm}
Bohužel se to zobrazí tak, že adresa je úplně na konci (až za submit buttonem) a
$this->addGroup("Adresa");
se nezobrazí vůbec
- Vojtěch Dobeš
- Gold Partner | 1316
#7: Myslíš pořadí prvků v tom address
balíčku? Pak už
ale de facto nechceš přidávat to samé, ale pokaždé „to samé v jiném
pořadí“… každopádně můžeš jednoduše ovlivnit, kde bude to samotné
address
. Pokud použiješ manuální vykreslování, můžeš to
pak naházet úplně kamkoliv… (zapouzdření do kontejneru bude čistě
logické na straně PHP).
- kedrigern
- Člen | 102
#11: Ne, myslím jen poskládání komponent za sebe. Což se řeší zde
Bohužel stále jsem nedosáhl jiného umístění, než zcela na konci formuláře.
Manuální vykreslování po komponentách by mi stačilo, ale vykreslovat vše ručně nechci (snažím se aplikaci zjednodušit, nikoliv zesložitit).
- Vojtěch Dobeš
- Gold Partner | 1316
Nepomohlo by sestavovat ten formulář až v attached()
při
napojení na presenter?
- Filip Procházka
- Moderator | 4668
@vojtech.dobes na to kápl :) Správně takto:
class AddressContainer extends Container
{
public function __construct()
{
parent::__construct();
$this->monitor('Nette\Forms\Form');
}
protected function attached($obj)
{
parent::attached($obj);
if (!$obj instanceof \Nette\Forms\Form) return;
$this->currentGroup = $this->form->currentGroup;
$this->addText("street", "Ulice:")
->addRule(Form::MAX_LENGTH, 'Jméno ulice nesmí být delší než %d znaků', 30);
//...
}
}
class RegistrationForm extends Form
{
public function __construct()
{
// ...
$this->addGroup("Adresa");
$this['address'] = new AddressContainer;
// ...
}
}
@llook: příště odkazuj tohle ;)
Na tohle bych doporučoval udělat si „opravený container“, který budeš obecně používat:
abstract class BaseContainer extends Nette\Forms\Container
{
public function __construct()
{
parent::__construct();
$this->monitor('Nette\Forms\Form');
}
protected function attached($obj)
{
parent::attached($obj);
if ($obj instanceof \Nette\Forms\Form) {
$this->currentGroup = $this->form->currentGroup;
$this->configure();
}
}
abstract protected function configure();
}
Použití by vypadalo takto:
class AddressContainer extends BaseContainer
{
protected function configure()
{
$this->addText("street", "Ulice:")
->addRule(Form::MAX_LENGTH, 'Jméno ulice nesmí být delší než %d znaků', 30);
//...
}
}
A použití zůstane jednoduché
class RegistrationForm extends Form
{
public function __construct()
{
parent::__construct();
// ...
$this->addGroup("Adresa");
$this['address'] = new AddressContainer;
// ...
}
}
Editoval HosipLan (12. 8. 2012 0:10)
- Filip Procházka
- Moderator | 4668
@Horyczech: bylo to kvůli chybějícímu volání
monitor()
, doplnil jsem.
- Filip Procházka
- Moderator | 4668
Doporučoval bych ti použít raději to řešení co mám pod čarou, protože to omezí duplikování kódu a vypadá to o dost lépe ;)