Forms Rendering
A formák megjelenése nagyon változatos lehet. A gyakorlatban két szélsőséggel találkozhatunk. Egyrészt szükség van
arra, hogy egy alkalmazásban egy sor, egymáshoz vizuálisan hasonló űrlapot jelenítsünk meg, és értékeljük a
$form->render()
segítségével történő egyszerű, sablon nélküli megjelenítést. Ez általában az
adminisztrációs felületek esetében fordul elő.
Másrészt vannak különböző űrlapok, ahol mindegyik egyedi. Megjelenésüket a legjobban a sablonban található HTML nyelv segítségével lehet leírni. És természetesen a két említett szélsőség mellett számos olyan űrlappal is találkozunk, amelyek valahol a kettő között helyezkednek el.
Renderelés Latte-val
A 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.
A Nette\Forms\Blueprint::latte($form)
metódus segítségével létrehozhatja a Latte sablon
javaslatát az űrlaphoz, amely a böngészőoldalra kimeneti azt. Ezután csak ki kell választania a kódot egy kattintással,
és be kell másolnia a projektjébe.
{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}
{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>
{form}
Címkék {form signInForm}...{/form}
alternatívája a
<form n:name="signInForm">...</form>
.
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 az űrlap belső részét kell megjelenítenie HTML címkék nélkül <form>
, például a snippetek
elküldésekor, rejtse el őket a n:tag-if
attribútummal:
<form n:name=signInForm n:tag-if=false>
<div>
<label n:name=username>Username: <input n:name=username></label>
{inputError username}
</div>
</form>
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}
Renderelés 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" required 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" required 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" required value=""></dd>
<dt><label class="required" for="frm-age">Age:</label></dt>
<dd><input type="text" class="text" name="age" id="frm-age" required 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);
A renderelő először a csoportokat rajzolja ki, majd azokat az elemeket, amelyek nem tartoznak egyik csoporthoz sem.
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
Az űrlapelemek tetszőleges HTML-attribútumainak beállításához használja a
setHtmlAttribute(string $name, $value = true)
metódust:
$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.
// A <form> attribútumainak beállítása.
$form->setHtmlAttribute('id', 'myForm');
Az elem típusának megadása:
$form->addText('tel', 'Your telephone:')
->setHtmlType('tel')
->setHtmlAttribute('placeholder', 'Please, fill in your telephone');
A típus és egyéb attribútumok beállítása csak vizuális célokat szolgál. A bevitel helyességének ellenőrzésének a kiszolgálón kell megtörténnie, amit egy megfelelő űrlapvezérlő kiválasztásával és érvényesítési szabályok megadásával biztosíthat.
A rádió- vagy jelölőnégyzet-listák egyes elemeihez beállíthatunk egy HTML-attribútumot, amelynek értékei eltérőek
lehetnek. Vegyük észre a kettőspontot a style:
után, ami biztosítja, hogy az értéket a kulcs alapján
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 boolean attribútumok beállításához, mint például a readonly
, használhatjuk a kérdőjellel ellátott
jelölést:
$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');
$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);
};