Form renderer – bootstrap tabs

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

Ahoj,

neřešili jste někdo automatický render formuláře do tabů?
Kvůli nim to budu muset řešit manuálně a připadá mi to celkem škoda.

Teoreticky by šlo využít group. Co group, to tab, či ne?

Jan Mikeš
Člen | 771
+
0
-

Můžeš si nachystat jednu šablonu s ručním vyrenderováním formuláře, kterou budou využívat všechny formy, já to tak používám pro všechny formuláře v admin rozhraní, viz. příklad, třeba pomůže/inspiruje.

{* translatableForm.latte: *}
{form $formName}
    {var $renderer = $form->getRenderer()}

    {$renderer->render($form, 'ownerrors')|noescape}

    <fieldset>
        {foreach $form["common"]->controls as $formControl}
            {$renderer->renderPair($formControl)|noescape}
        {/foreach}
    </fieldset>

    <ul class="nav nav-tabs" n:inner-foreach="$form['translations']->components as $locale => $container">
        <li n:class="$iterator->first? active">
            <a data-toggle="tab" href="#tab-lang-{$locale}">
                {$locale}
            </a>
        </li>
    </ul>

    <div class="tab-content" n:inner-foreach="$form['translations']->components as $locale => $container">
        <fieldset n:class="$iterator->first? 'active in', tab-pane, fade" id="tab-lang-{$locale}" n:inner-foreach="$container->controls as $formControl">
            {$renderer->renderPair($formControl)|noescape}
        </fieldset>
    </div>

    <fieldset>
        {foreach $form["buttons"]->controls as $formControl}
            {$renderer->renderPair($formControl)|noescape}
        {/foreach}
    </fieldset>
{/form}
{* etc Actuality.add.latte *}
{include 'translatableForm.latte', formName => 'manageActualityForm'}

U všech formulářů v admin pak dodržuji strukturu, kdy mám 3 containery, shared, translations a buttons. Výsledné formuláře pak vypadají např. takto
V tomto případě jsou navíc jako bonus otevírány ajaxem v modalu.

Landsman
Člen | 152
+
0
-

@Lexi To vypadá pěkně.
O getRenderer() jsem nevěděl.
Díky.

Co používáš za renderer?
Ten od tomaj nevykreslí pěkně renderPair() dle tvé ukázky.

Editoval Landsman (8. 9. 2016 20:58)

Jan Mikeš
Člen | 771
+
0
-

Mám BaseFormFactory s tímto kódem:

public function create($domain = NULL)
	{
		$form = new UI\Form;

		$renderer = $form->getRenderer();
		$renderer->wrappers['controls']['container'] = NULL;
		$renderer->wrappers['pair']['container'] = 'div class=control-pair';
		$renderer->wrappers['pair']['.error'] = 'has-error';
		$renderer->wrappers['control']['container'] = 'div class=control-input';
		$renderer->wrappers['label']['container'] = 'div class=control-label';
		$renderer->wrappers['control']['description'] = 'span class=input-help';
		$renderer->wrappers['control']['errorcontainer'] = 'span class=form-error-message';

		// make form and controls compatible with Twitter Bootstrap
		foreach ($form->getControls() as $control) {
			if ($control instanceof Controls\Checkbox || $control instanceof Controls\CheckboxList || $control instanceof Controls\RadioList) {
				$control->getSeparatorPrototype()->setName('div')->addClass($control->getControlPrototype()->type);
			}
		}

		return $form;
	}

Zde lehce očesaný kód pro LESS:

form {
	.control-pair {
		.make-row(0);
		.clearfix();

		&.has-error {
			.control-label {
				color: red;
			}
		}

		&.required {
			.control-label {
				label:before {
					content: '* ';
					color: red;
				}
			}
		}
	}

	.control-label {
		.make-sm-column(3, 0);
	}

	.control-input {
		.make-sm-column(9, 0);
	}

	.input-help {
		// ...
	}

	.input-error {
		// ...
	}

	select,
	input[type="text"],
	input[type="date"],
	input[type="email"],
	input[type="password"],
	textarea {
		.form-control;
		// ...
	}

	input[type="submit"] {
		.btn;
		.btn-success;
	}
}

Ve skutečnosti je toho CSS/LESS mnohem více :-)

Editoval Lexi (8. 9. 2016 21:54)

Landsman
Člen | 152
+
0
-

Nepoužil jsi tam náhodou někdy i replicator?
Řeším to, že bych jeden z těch tabů chtěl překreslovat po přidání položky replicatorem. Tzn obalit jej snippetem. Taby si ručně vypíši, nic jiného mi asi nezbyde. Pak je tam ale tento problém: https://forum.nette.org/…a-form-maker#…

Jan Mikeš
Člen | 771
+
0
-

Bohužel replicator nepoužívám, ale řešením by mohla být kombinace toho co tam píší. Pravděpodobně budeš muset použít {snippetArea} kvůli includované šabloně, dále {var $_form} hack, název formuláře máš v proměnné $formName, takže se potřebuješ dostat k {$control[$formName]}

Landsman
Člen | 152
+
0
-

@Lexi K formuláři jsem se dostal takto:

{var form = $_args['_control']->components['itemEditForm']}

… až sem se zděsil, co je v tom $_args dat, divím se, že je to tak rychlé…

Editoval Landsman (8. 9. 2016 22:45)

Jan Mikeš
Člen | 771
+
0
-

Je to ošklivý hack, ale funkční :-) alternativou by mohlo být javascriptové řešení, které by ti duplikovalo containery/inputy, ale napsat by sis ho musel sám (možná na fóru budou ukázky kódů).

Výhodou tohoto řešení by byl celkově čistější přístup, rychlost (úplně by vypadla potřeba kontaktovat server při přidávání kontejnerů, vše by řešil JS).

Nevýhodou mírně složitější zpracování dat přes getHttpData() a nějaké to psaní v javascriptu.

Landsman
Člen | 152
+
0
-

Lexi napsal(a):

Je to ošklivý hack, ale funkční :-) alternativou by mohlo být javascriptové řešení, které by ti duplikovalo containery/inputy, ale napsat by sis ho musel sám (možná na fóru budou ukázky kódů).

Výhodou tohoto řešení by byl celkově čistější přístup, rychlost (úplně by vypadla potřeba kontaktovat server při přidávání kontejnerů, vše by řešil JS).

Nevýhodou mírně složitější zpracování dat přes getHttpData() a nějaké to psaní v javascriptu.

Já to na jednom projektu mám tak, jak popisuješ. Realizace byla upřímně o dost snazší, ale chtěl jsem to tady zkusit udělat přes replicator. Hotovo. Teď přemýšlím, jaké mi to může dát výhody oproti té jquery manipulaci s DOM :D

No teoreticky to můžu ukládat do session, kdyby třeba spadl internet :)

Editoval Landsman (8. 9. 2016 22:59)

Jan Mikeš
Člen | 771
+
0
-

Landsman napsal(a):

No teoreticky to můžu ukládat do session, kdyby třeba spadl internet :)

To konec konců můžeš i s tou JS implementací ;) pokaždé když zmáčkneš tlačítko „přidat položku“ nebo „odebrat položku“, může se na pozadí spustit ajax na signál, který ti přidá do session informaci o tom, že uživatel přidal nový kontejner. To samé s odebíráním. Do toho ještě v několika vteřinových intervalech ukládat data uživatele a po odeslání formuláře session vyčistit (k tomu mám případně také někde ukázku, u formuláře který má kolem 100 inputů se to velice hodí :-) ).

Fantazii se meze nekladou.

Landsman
Člen | 152
+
0
-

Lexi napsal(a):

Landsman napsal(a):

No teoreticky to můžu ukládat do session, kdyby třeba spadl internet :)

To konec konců můžeš i s tou JS implementací ;) pokaždé když zmáčkneš tlačítko „přidat položku“ nebo „odebrat položku“, může se na pozadí spustit ajax na signál, který ti přidá do session informaci o tom, že uživatel přidal nový kontejner. To samé s odebíráním. Do toho ještě v několika vteřinových intervalech ukládat data uživatele a po odeslání formuláře session vyčistit (k tomu mám případně také někde ukázku, u formuláře který má kolem 100 inputů se to velice hodí :-) ).

Fantazii se meze nekladou.

Cool, tam se to rozhodně vyplatí.
Vychází z toho ten JS asi opravdu nejlépe. Příště asi komplet přes něj.