Dynamicky generovaný formulář

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

Zdravím pánové a dámy ;)
Řeším teď do své appky jak dostat dynamické elementy do základního formuláře. Situace je taková že v si nadefinuju nějaký typ položky, dejme tomu článek a zadefinuju že bude mít x políček typu text, x políček typu obrázek atd. Takže v db či souboru si uložím jaké formulářové elementy budou u toho článku využity.

A teď situace kdy chci přidat nový článek, otevře se mi základní form se základními prvky (název, kategorie, meta) a k těmto prvkům potřebuji přidat ty dynamicky zadefinované, tedy x políček typu text a x políček typu obrázek.

V předhozí nonNette aplikaci jsem to řešil tak že jsem si vytvářel objekty FormElements kde každé nové políčko představovalo vlastní objekt, v podstatě takový miniform s vlastní šablonou pro vykreslení ve formulář a vlastní šablonou pro vykreslení samotného obsahu už na stránce s detailem položky.

Jak by bylo nejvhodnější toto řešit v nette? Udělat nějakou miniformtovárničku která by reprezentovala jeden element? (jeden element může obsahovat více formulářových prvků)
Zároveň ale potřebuji i zadefinovat jak se bude vykreslovat samotný obsah při zobrazení položky takže by to měl být nějaký obsáhlejší objekt co by dokázal vytvářet formulářové prvky daného elementu, měl by dokázat je renderovat do formuláře a měl by také dokázat je renderovat do obsahu.

romiix.org
Člen | 343
+
0
-

Prednedávnom som niečo podobné riešil. Použil som na to HosipLanov Replikátor.

Vytvoril som hlavný formulár do ktorého bol pridaný pomocou addDynamic() ďalší veľký formulár obsahujúci prepínač s názvami formulárových prvkov (selectbox, text, number, …) a pomocou toggle() sa zobrazovali/skrývali príslušné časti formulára.

Ukážka hlavného formulára

// ...
$self = $this;

$invalidateCallback = function () use ($presenter) {
	$presenter->invalidateControl('entireForm');
};
$form_items = $this->addDynamic('form_items', function (Container $form)use($self,$invalidateCallback) {
	$form->addRadioList("form_type", "Formulárový prvok",  array(
		"text" => "Textové pole",
		"number" => "Textové pole - číslo",
		"radiobox" => "Prepínač",
		"selectbox" => "Výberové pole",
	))
		->addCondition(Form::EQUAL, "text")
			->toggle($form->name."-text")
		->endCondition()
		->addCondition(Form::EQUAL, "number")
			->toggle($form->name."-number")
		->endCondition()
		->addCondition(Form::IS_IN,array("radiobox", "selectbox"))
			->toggle($form->name."-multiples");
	$form['text'] = new InputContainer();
	$form['text']->name = $form->name;
	$form['text']->attached($self);
	$form['number'] = new NumberContainer();
	$form['number']->name = $form->name;
	$form['number']->attached($self);
	$form['multiples'] = new MultiplesContainer();
	$form['multiples']->name = $form->name;
	$form['multiples']->attached($self);
	$form->addSubmit('remove', 'Odstrániť formulárový prvok')
		->setValidationScope(FALSE)
		->setAttribute('class', 'ajax')
		->addRemoveOnClick($invalidateCallback);
});
$form_items->addSubmit('add', 'Pridať formulárový prvok')
		->setValidationScope(FALSE)
		->setAttribute('class', 'ajax')
		->addCreateOnClick($invalidateCallback);
// ...

Trieda InputContainer.php

<?php
use Nette\Forms\Container,
	Nette\Utils\Html;

class InputContainer extends Container
{
	/** @var string */
	public $name;

	public function attached($obj)
	{
		parent::attached($obj);
		if (!$obj instanceof Nette\Forms\Form) return ;
		$this->addMarkup('xstart', "", Html::el('div')->id($this->name."-text")->startTag(), TRUE);
		$this->addText("name", "Názov");
		$this->addText("default_value", "Predvolená hodnota");
		$this->addText("placeholder", "Placeholder");
		$this->addCheckbox("required", "Povinná položka");
		$this->addMarkup('xend', "", Html::el('div')->id($this->name."-text")->endTag(), TRUE);
	}
}

Ostatné vyzerajú obdobne.

Ako vidno z ukážky, nepodarilo sa mi dokonale vyriešiť toggle() a tak som musel „dokresliť“ do formulára ohraničujúce <div>.

Na to som použil MarkupControl, ktorý som trochu upravil aby ním bolo možné vykresliť ľubovolný kód. ($onlyContent = FALSE)
Takto som to riešil, aby som nemusel manuálne renderovať obrovský formulár.

Asi by sa hodil na tento účel doplnok s podporou všetkých formulárov so všetkými možnosťami. Viem si predstaviť, že by som nabudúce iba spustil composer.

Editoval romiix.org (20. 11. 2013 11:25)

akadlec
Člen | 1326
+
0
-

@romiix.org: hmm zajímavý nápad. V podstatě bych to mohl řešit těmi kontejnery jak to máš ty akorat ještě nad nimi asi udělat nějakou obálku do které bych předal jen seznam např. „text“, „image“,„gallery“,„link“ a ona by ty jednotlivé kontejnery popřipojovala. Replicator tam nepotřebuju protože už dopředu vím jaké tam budou použity, ty se „naklikají“ jinde.

Díky za nakopnutí.

akadlec
Člen | 1326
+
0
-

Takže sem se pokusil včera něco dát dohromady. Vycházel sem z toho jak funguje replicator a vytřil si něco podobného, předám si tam seznam těch jednotlivých elementů a z těch si to pak uvnitř poskládám. Supr funguje to, ale teď řeším jak udělat vlastní render.

Když udělám vykreslení formuláře jako control je to ok, vykreslí se všechny pole. Ale já formy vykresluju ručně protože je mám rozdělené na bloky atd. no a co u toho řeším je jak udělat vykreslení té dynamické části? Libilo by se mě kdybych mohl každému elementu určit vlastní minilatte šablonu kterou by se vykreslil takže abych mohl pak ve latte šabloně akce mohl udělat něco takového:

{form dynamickyForm}
....{label neco}....
... klasicke prvky formu...

{* Vygenerovani vsech elementu *}
{$form['elements']}

{* Nebo v cyklu *}
{foreach $form['elements'] as $element}
<div class="element">
	{$element}
</div>
{/foreach}
{/form dynamickyForm}

To znamená dostat do těch kontejneru nějaký vlastní renderer který by bral šablony z nějakého default umístění, ty vyrenderoval a vrátil už hotové

Nějaký tip, rada atd.?