Forms Rendering

Az űrlapok megjelenése nagymértékben eltérhet egymástól. Valójában két szélsőség létezik. Az egyik oldal az, amikor egy sor nagyon hasonló űrlapot kell újra és újra megjeleníteni, kevés vagy semmi erőfeszítéssel. Általában adminisztrációk és back-endek.

A másik oldal az apró, édes formák, amelyek mindegyike egy-egy műalkotás. Ezek elrendezése a legjobban HTML-ben írható meg. Természetesen ezeken a végleteken kívül is sok űrlap létezik a kettő között.

Latte

Latte templating rendszer alapvetően megkönnyíti az űrlapok és elemeik megjelenítését. Először megmutatjuk, hogyan lehet az űrlapokat manuálisan, elemenként renderelni, hogy teljes kontrollt kapjunk a kód felett. Később megmutatjuk, hogyan lehet automatizálni ezt a renderelést.

{control}

A legegyszerűbb módja egy űrlap megjelenítésének, ha egy sablonba írjuk:

{control signInForm}

A renderelt űrlap kinézete a Renderer és az egyes vezérlőelemek konfigurálásával változtatható.

n:name

Rendkívül egyszerű a PHP-kódban lévő űrlapdefiníciót HTML-kóddal összekapcsolni. Csak hozzá kell adni a n:name attribútumokat. Ennyire egyszerű!

protected function createComponentSignInForm(): Form
{
	$form = new Form;
	$form->addText('username')->setRequired();
	$form->addPassword('password')->setRequired();
	$form->addSubmit('send');
	return $form;
}
<form n:name=signInForm class=form>
	<div>
		<label n:name=username>Username: <input n:name=username size=20 autofocus></label>
	</div>
	<div>
		<label n:name=password>Password: <input n:name=password></label>
	</div>
	<div>
		<input n:name=send class="btn btn-default">
	</div>
</form>

Az eredményül kapott HTML-kód kinézete teljes mértékben az Ön kezében van. Ha a n:name attribútumot használja a <select>, <button> vagy a <textarea> elemeket használ, azok belső tartalma automatikusan kitöltődik. Ezen kívül a <form n:name> tag létrehoz egy helyi változót $form a rajzolt űrlapobjektummal és a záró </form> kirajzolja az összes ki nem rajzolt rejtett elemet (ugyanez vonatkozik a {form} ... {/form}).

Nem szabad azonban megfeledkeznünk az esetleges hibaüzenetek megjelenítéséről sem. Mindazokat, amelyeket a addError() módszerrel (a {inputError} segítségével), mind azokat, amelyeket a módszerrel adtunk hozzá az egyes elemekhez, és azokat is, amelyeket közvetlenül az űrlaphoz adtunk hozzá (a $form->getOwnErrors() által visszaadottak):

<form n:name=signInForm class=form>
	<ul class="errors" n:ifcontent>
		<li n:foreach="$form->getOwnErrors() as $error">{$error}</li>
	</ul>

	<div>
		<label n:name=username>Username: <input n:name=username size=20 autofocus></label>
		<span class=error n:ifcontent>{inputError username}</span>
	</div>
	<div>
		<label n:name=password>Password: <input n:name=password></label>
		<span class=error n:ifcontent>{inputError password}</span>
	</div>
	<div>
		<input n:name=send class="btn btn-default">
	</div>
</form>

Az összetettebb űrlapelemek, mint például a RadioList vagy CheckboxList, elemenként is megjeleníthetők:

{foreach $form[gender]->getItems() as $key => $label}
	<label n:name="gender:$key"><input n:name="gender:$key"> {$label}</label>
{/foreach}

Kódjavaslat {formPrint}

Hasonló Latte kódot generálhat egy űrlaphoz a {formPrint} tag használatával. Ha ezt egy sablonba helyezi, a normál megjelenítés helyett a kódtervezetet fogja látni. Ezután csak válassza ki, és másolja be a projektjébe.

{label} & {input}

Nem akarja átgondolni, hogy minden egyes elemhez milyen HTML elemet használjon a sablonban, akár <input>, <textarea> stb. A megoldás az univerzális {input} tag:

<form n:name=signInForm class=form>
	<ul class="errors" n:ifcontent>
		<li n:foreach="$form->getOwnErrors() as $error">{$error}</li>
	</ul>

	<div>
		{label username}Username: {input username, size: 20, autofocus: true}{/label}
		{inputError username}
	</div>
	<div>
		{label password}Password: {input password}{/label}
		{inputError password}
	</div>
	<div>
		{input send, class: "btn btn-default"}
	</div>
</form>

Ha az űrlap fordítót használ, a {label} címkékben lévő szöveg le lesz fordítva.

Az összetettebb űrlapelemek, mint például a RadioList vagy CheckboxList, elemenként is megjeleníthetők:

{foreach $form[gender]->items as $key => $label}
	{label gender:$key}{input gender:$key} {$label}{/label}
{/foreach}

A <input> magának a Checkbox elemben való megjelenítéséhez használja a {input myCheckbox:} címet. A HTML-attribútumokat vesszővel kell elválasztani {input myCheckbox:, class: required}.

{inputError}

Hibaüzenetet ír ki az űrlapelemhez, ha van ilyen. Az üzenet általában egy HTML-elembe van csomagolva a formázás érdekében. Az üres elem megjelenítésének elkerülése, ha nincs üzenet, elegánsan megoldható a n:ifcontent segítségével:

<span class=error n:ifcontent>{inputError $input}</span>

A hasErrors() metódus segítségével észlelhetjük a hiba jelenlétét, és ennek megfelelően állíthatjuk be a szülőelem osztályát:

<div n:class="$form[username]->hasErrors() ? 'error'">
	{input username}
	{inputError username}
</div>

Automatikus renderelés

A {input} és {label} címkékkel könnyen létrehozhatunk egy általános sablont bármilyen űrlaphoz. Ez az összes elemét szekvenciálisan végigjárja és megjeleníti, kivéve a rejtett elemeket, amelyek automatikusan megjelenítésre kerülnek, amikor az űrlapot a </form> címkével zárul. A megjelenített űrlap nevét a $form változóban várja el.

<form n:name=$form class=form>
	<ul class="errors" n:ifcontent>
		<li n:foreach="$form->getOwnErrors() as $error">{$error}</li>
	</ul>

	<div n:foreach="$form->getControls() as $input"
		n:if="$input->getOption(type) !== hidden">
		{label $input /}
		{input $input}
		{inputError $input}
	</div>
</form>

A használt önzáró párcímkék {label .../} a PHP kódban az űrlap definíciójából származó címkéket jelenítik meg.

Ezt az általános sablont elmentheti a basic-form.latte fájlba, és az űrlap megjelenítéséhez csak be kell építenie, és át kell adnia az űrlap nevét (vagy példányát) a $form paraméterhez:

{include basic-form.latte, form: signInForm}

Ha egy adott űrlap megjelenését szeretnénk befolyásolni, és egy elemet másképp rajzolni, akkor a legegyszerűbb, ha a sablonban olyan blokkokat készítünk, amelyeket később felülírhatunk. A blokkok dinamikus nevet is kaphatnak, így a kirajzolandó elem nevét is beillesztheti beléjük. Például:

...
	{label $input /}
	{block "input-{$input->name}"}{input $input}{/block}
...

A pl. username elemhez ez létrehozza a input-username blokkot, amely könnyen felülírható a {embed} tag használatával:

{embed basic-form.latte, form: signInForm}
	{block input-username}
		<span class=important>
			{include parent}
		</span>
	{/block}
{/embed}

Alternatívaként a basic-form.latte sablon teljes tartalma definiálható blokkként, beleértve a $form paramétert is:

{define basic-form, $form}
	<form n:name=$form class=form>
		...
	</form>
{/define}

Ez némileg megkönnyíti a használatát:

{embed basic-form, signInForm}
	...
{/embed}

A blokkot csak egy helyre kell importálnia, az elrendezési sablon elejére:

{import basic-form.latte}

Speciális esetek

Ha csak egy űrlap belső tartalmát kell megjelenítenie anélkül, hogy a <form> & </form> HTML-címkéket, például egy AJAX-kérés során, akkor a {formContext} … {/formContext} segítségével nyithatja meg és zárhatja be az űrlapot. Logikai értelemben hasonlóan működik, mint a {form}, itt lehetővé teszi, hogy más címkéket használjon az űrlapelemek megrajzolásához, ugyanakkor nem rajzol semmit.

{formContext signForm}
	<div>
		<label n:name=username>Username: <input n:name=username></label>
		{inputError username}
	</div>
{/formContext}

A formContainer címke az űrlapkonténeren belüli bemenetek megjelenítését segíti.

<p>Which news you wish to receive:</p>

{formContainer emailNews}
<ul>
	<li>{input sport} {label sport /}</li>
	<li>{input science} {label science /}</li>
</ul>
{/formContainer}

Latte nélkül

A legegyszerűbb módja egy űrlap megjelenítésének a következő hívás:

$form->render();

A renderelt űrlap kinézete a Renderer és az egyes vezérlőelemek konfigurálásával változtatható.

Kézi renderelés

Minden űrlapelem rendelkezik olyan metódusokkal, amelyek az űrlapmező és a címke HTML-kódját generálják. A metódusok vagy egy karakterlánc vagy egy Nette\Utils\Html objektum formájában adhatják vissza:

  • getControl(): Html|string az elem HTML kódját adja vissza
  • A getLabel($caption = null): Html|string|null a címke HTML-kódját adja vissza, ha van ilyen.

Ez lehetővé teszi az űrlap elemenkénti megjelenítését:

<?php $form->render('begin') ?>
<?php $form->render('errors') ?>

<div>
	<?= $form['name']->getLabel() ?>
	<?= $form['name']->getControl() ?>
	<span class=error><?= htmlspecialchars($form['name']->getError()) ?></span>
</div>

<div>
	<?= $form['age']->getLabel() ?>
	<?= $form['age']->getControl() ?>
	<span class=error><?= htmlspecialchars($form['age']->getError()) ?></span>
</div>

// ...

<?php $form->render('end') ?>

Míg néhány elem esetében a getControl() egyetlen HTML-elemet ad vissza (pl. <input>, <select> stb.), mások esetében egy egész HTML-kódot ad vissza (CheckboxList, RadioList). Ebben az esetben használhat olyan metódusokat, amelyek külön-külön generálnak beviteli és címkézési adatokat, minden egyes elemhez külön-külön:

  • getControlPart($key = null): ?Html egy elem HTML-kódját adja vissza.
  • getLabelPart($key = null): ?Html visszaadja egy elem címkéjének HTML-kódját.

Ezek a metódusok történelmi okokból a get előtaggal vannak ellátva, de a generate jobb lenne, mivel minden egyes híváskor egy új Html elemet hoz létre és ad vissza.

Renderer

Ez egy objektum, amely az űrlap renderelését biztosítja. A $form->setRenderer metódussal lehet beállítani. A $form->render() metódus meghívásakor adják át a vezérlést.

Ha nem állítunk be egyéni renderelőt, akkor az alapértelmezett renderelőt Nette\Forms\Rendering\DefaultFormRenderer fogja használni. Ez az űrlap elemeit HTML táblázatként rendereli. A kimenet így néz ki:

<table>
<tr class="required">
	<th><label class="required" for="frm-name">Name:</label></th>

	<td><input type="text" class="text" name="name" id="frm-name" value=""></td>
</tr>

<tr class="required">
	<th><label class="required" for="frm-age">Age:</label></th>

	<td><input type="text" class="text" name="age" id="frm-age" value=""></td>
</tr>

<tr>
	<th><label>Gender:</label></th>
	...

Sok webdesigner más jelöléseket, például listát preferál. Beállíthatjuk a DefaultFormRenderer címet úgy, hogy egyáltalán ne rendereljük táblázatba. Csak megfelelő $wrappereket kell beállítanunk. Az első index mindig egy területet, a második pedig egy elemet jelöl. A képen az összes megfelelő terület látható:

Alapértelmezés szerint a controls egy csoportja be van csomagolva egy <table>, és minden egyes pair egy táblázat sora <tr> amely a label és a control egy párját tartalmazza (cellák <th> és <td>). Változtassuk meg ezeket a burkolóelemeket. A controls -t be fogjuk csomagolni <dl>, a pair -t önmagában hagyjuk, a label -t a következőbe tesszük <dt> és a control -t a következőbe csomagoljuk <dd>:

$renderer = $form->getRenderer();
$renderer->wrappers['controls']['container'] = 'dl';
$renderer->wrappers['pair']['container'] = null;
$renderer->wrappers['label']['container'] = 'dt';
$renderer->wrappers['control']['container'] = 'dd';

$form->render();

Az eredmény a következő részlet:

<dl>
	<dt><label class="required" for="frm-name">Name:</label></dt>

	<dd><input type="text" class="text" name="name" id="frm-name" value=""></dd>


	<dt><label class="required" for="frm-age">Age:</label></dt>

	<dd><input type="text" class="text" name="age" id="frm-age" value=""></dd>


	<dt><label>Gender:</label></dt>
	...
</dl>

A burkolók számos attribútumot befolyásolhatnak. Például:

  • speciális CSS osztályok hozzáadása minden egyes űrlapbemenethez
  • megkülönböztetni a páratlan és páros sorokat
  • a kötelező és opcionális elemek eltérő rajzolása
  • állítsa be, hogy a hibaüzenetek az űrlap felett vagy az egyes elemek közelében jelenjenek meg.

Opciók

A Renderer viselkedése az egyes űrlapelemek opciók beállításával is szabályozható. Így beállítható a beviteli mező mellett megjelenő tooltip:

$form->addText('phone', 'Number:')
	->setOption('description', 'This number will remain hidden');

Ha HTML tartalmat szeretnénk elhelyezni benne, akkor a Html osztályt használjuk.

use Nette\Utils\Html;

$form->addText('phone', 'Phone:')
	->setOption('description', Html::el('p')
		->setHtml('<a href="...">Terms of service.</a>')
	);

Html elem is használható a címke helyett: $form->addCheckbox('conditions', $label).

Bemenetek csoportosítása

A renderelő lehetővé teszi az elemek vizuális csoportokba (mezőcsoportokba) való csoportosítását:

$form->addGroup('Personal data');

Új csoport létrehozása aktiválja azt – minden további hozzáadott elemet hozzáad ehhez a csoporthoz. Egy űrlapot így építhetünk fel:

$form = new Form;
$form->addGroup('Personal data');
$form->addText('name', 'Your name:');
$form->addInteger('age', 'Your age:');
$form->addEmail('email', 'Email:');

$form->addGroup('Shipping address');
$form->addCheckbox('send', 'Ship to address');
$form->addText('street', 'Street:');
$form->addText('city', 'City:');
$form->addSelect('country', 'Country:', $countries);

Bootstrap támogatás

Példákat talál a Twitter Bootstrap 2, Bootstrap 3 és Bootstrap 4 Renderer konfigurációjára.

HTML attribútumok

A setHtmlAttribute(string $name, $value = true) segítségével bármilyen HTML-attribútumot beállíthat az űrlapvezérlőkhöz:

$form->addInteger('number', 'Szám:')
	->setHtmlAttribute('class', 'big-number');

$form->addSelect('rank', 'Order by:', ['price', 'name'])
	->setHtmlAttribute('onchange', 'submit()'); // változáskor meghívja a submit() JS függvényt.


// <form>-ra való alkalmazás
$form->setHtmlAttribute('id', 'myForm');

Input type beállítása:

$form->addText('tel', 'Your telephone:')
	->setHtmlType('tel')
	->setHtmlAttribute('placeholder', 'Please, fill in your telephone');

HTML attribútumot állíthatunk be a rádió- vagy jelölőnégyzet-listák egyes elemeihez, mindegyikhez különböző értékekkel. Figyeljük meg a kettőspontot a style: után, hogy az értéket a kulcs szerint válasszuk ki:

$colors = ['r' => 'red', 'g' => 'green', 'b' => 'blue'];
$styles = ['r' => 'background:red', 'g' => 'background:green'];
$form->addCheckboxList('colors', 'Colors:', $colors)
	->setHtmlAttribute('style:', $styles);

Renders:

<label><input type="checkbox" name="colors[]" style="background:red" value="r">red</label>
<label><input type="checkbox" name="colors[]" style="background:green" value="g">green</label>
<label><input type="checkbox" name="colors[]" value="b">blue</label>

A logikai HTML-attribútumok (amelyeknek nincs értéke, mint például a readonly) esetében kérdőjelet használhat:

$colors = ['r' => 'red', 'g' => 'green', 'b' => 'blue'];
$form->addCheckboxList('colors', 'Colors:', $colors)
	->setHtmlAttribute('readonly?', 'r'); // több kulcs esetén használjunk tömböt, pl. ['r', 'g']

Renders:

<label><input type="checkbox" name="colors[]" readonly value="r">red</label>
<label><input type="checkbox" name="colors[]" value="g">green</label>
<label><input type="checkbox" name="colors[]" value="b">blue</label>

A selectboxok esetében a setHtmlAttribute() módszer beállítja az attribútumokat a <select> elemhez. Ha az attribútumokat minden egyes <option>, a setOptionAttribute() módszert fogjuk használni. A fent használt kettőspont és kérdőjel is működik:

$form->addSelect('colors', 'Colors:', $colors)
	->setOptionAttribute('style:', $styles);

Renders:

<select name="colors">
	<option value="r" style="background:red">red</option>
	<option value="g" style="background:green">green</option>
	<option value="b">blue</option>
</select>

Prototípusok

A HTML-attribútumok beállításának alternatív módja a sablon módosítása, amelyből a HTML-elem generálódik. A sablon egy Html objektum, és a getControlPrototype() metódus adja vissza:

$input = $form->addInteger('number');
$html = $input->getControlPrototype(); // <input>
$html->class('big-number'); // <input class="big-number">

A getLabelPrototype() által visszaadott címke sablon is módosítható ilyen módon:

$html = $input->getLabelPrototype(); // <label>
$html->class('distinctive'); // <label class="distinctive">

A Checkbox, CheckboxList és RadioList elemek esetében befolyásolhatja az elemet körülvevő elemsablont. Ezt a getContainerPrototype() adja vissza. Alapértelmezés szerint ez egy “üres” elem, tehát semmi sem kerül megjelenítésre, de ha nevet adunk neki, akkor megjelenítésre kerül:

$input = $form->addCheckbox('send');
echo $input->getControl();
// <label><input type="checkbox" name="send"></label>

$html = $input->getContainerPrototype();
$html->setName('div'); // <div>
$html->class('check'); // <div class="check">
echo $input->getControl();
// <div class="check"><label><input type="checkbox" name="send"></label></div>

A CheckboxList és RadioList esetében a getSeparatorPrototype() metódus által visszaadott elemelválasztó mintát is lehet befolyásolni. Alapértelmezés szerint ez egy elem <br>. Ha ezt páros elemre változtatjuk, akkor az egyes elemeket elválasztás helyett be fogja burkolni. Lehetőség van az elemcímkék HTML elemsablonjának befolyásolására is, amely a getItemLabelPrototype() metódust adja vissza.

Fordítás

Ha többnyelvű alkalmazást programoz, valószínűleg különböző nyelveken kell megjelenítenie az űrlapot. A Nette Framework erre a célra egy fordítási felületet definiál Nette\Localization\Translator. A Nette-ben nincs alapértelmezett implementáció, igényei szerint választhat több kész megoldás közül, amelyeket a Componette-en talál. A dokumentációjukból megtudhatja, hogyan kell a fordítót konfigurálni.

Az űrlap támogatja a szövegek kiadását a fordítóprogramon keresztül. Ezt a setTranslator() metódus segítségével adjuk át:

$form->setTranslator($translator);

Mostantól kezdve nemcsak az összes címke, hanem az összes hibaüzenet vagy a kiválasztó dobozok bejegyzései is le lesznek fordítva egy másik nyelvre.

Lehetőség van az egyes űrlapelemekhez más fordítót beállítani, vagy a fordítást teljesen kikapcsolni a null címmel:

$form->addSelect('carModel', 'Model:', $cars)
	->setTranslator(null);

Az érvényesítési szabályok esetében a fordítónak specifikus paramétereket is át kell adni, például a szabály esetében:

$form->addPassword('password', 'Password:')
	->addRule($form::MinLength, 'Password has to be at least %d characters long', 8)

A fordítót a következő paraméterekkel hívjuk meg:

$translator->translate('Password has to be at least %d characters long', 8);

így a characters szóhoz a megfelelő többes számot tudja kiválasztani.

Esemény onRender

Közvetlenül az űrlap renderelése előtt meghívhatjuk a kódunkat. Ez például HTML-osztályokat adhat az űrlap elemeihez a megfelelő megjelenítés érdekében. A kódot a onRender tömbhöz adjuk hozzá:

$form->onRender[] = function ($form) {
	BootstrapCSS::initialize($form);
};
verzió: 4.0