Skládaní komponent a formuláře

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

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.

vvoody
Člen | 910
+
0
-

Vyuzi dedicnost tried.

class BaseForm extends Nette\Application\UI\Form{

}
class MyForm extends BaseForm{

}

Precisti sa ti tak presenter. V tovarnickach ti tak zostane mozno len vzdy jeden riadok
return new MyForm

kedrigern
Člen | 102
+
0
-

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
+
0
-

Ten tvůj kód by měl fungovat, pokud bys v createComponentAddress místo new Form použil new Container (addContainer vytváří právě objekt třídy Nette\Forms\Container).

llook
Člen | 407
+
0
-

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
+
0
-

#4: Mě to tedy vyhodí stejnou výjimku.

kedrigern
Člen | 102
+
0
-

#5: Což mě nedovolí přeházet pořadí komponent při jednoduchém výpisu (nebo o tom nevím).

Moje snaha byla zjednodušit kód, čili asi nic.

kedrigern
Člen | 102
+
0
-

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

Horyczech
Člen | 6
+
0
-

Jukni sem

kedrigern
Člen | 102
+
0
-

Napsal jsem tam, nefunguje mi to.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

#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
+
0
-

#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
+
0
-

Nepomohlo by sestavovat ten formulář až v attached() při napojení na presenter?

Filip Procházka
Moderator | 4668
+
0
-

@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)

Horyczech
Člen | 6
+
0
-

@**HosipLan**: s tím attached() mi to nefunguje :( Položky z AddressContainer se ve formuláři neobjeví.

Filip Procházka
Moderator | 4668
+
0
-

@Horyczech: bylo to kvůli chybějícímu volání monitor(), doplnil jsem.

Horyczech
Člen | 6
+
0
-

@**HosipLan**: super, tedka se polozky pridaji, ale ne do te groupy..
Ja to mam resene takto
Ale libilo se mi Tve reseni.. jeste nez jsi to doplnil o monitor() :D

Filip Procházka
Moderator | 4668
+
0
-

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 ;)

Horyczech
Člen | 6
+
0
-

@**HosipLan**: oki, mam opraveno a funguje to jak ma.. diky ;)

kedrigern
Člen | 102
+
0
-

Též děkuji, funguje.