Nette Documentation Preview

syntax
Izrisovanje obrazcev
********************
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Izrisovanje obrazcev

Videz obrazcev je lahko zelo raznolik. V praksi lahko naletimo na dva ekstrema. Na eni strani stoji potreba po izrisovanju številnih obrazcev v aplikaciji, ki so si vizualno podobni kot jajce jajcu, in cenimo enostavno izrisovanje brez predloge s pomočjo $form->render(). Gre običajno za primer administrativnih vmesnikov.

Na drugi strani pa so raznoliki obrazci, kjer velja: vsak kos je original. Njihovo podobo najbolje opišemo z jezikom HTML v predlogi obrazca. In seveda poleg obeh omenjenih ekstremov naletimo na veliko obrazcev, ki se gibljejo nekje vmes.

Izrisovanje s pomočjo Latte

Sistem predlog Latte bistveno olajša izrisovanje obrazcev in njihovih elementov. Najprej si bomo pokazali, kako obrazce izrisovati ročno po posameznih elementih in s tem pridobiti popoln nadzor nad kodo. Kasneje si bomo pokazali, kako je mogoče takšno izrisovanje avtomatizirati.

Načrt Latte predloge obrazca si lahko pustite generirati s pomočjo metode Nette\Forms\Blueprint::latte($form), ki ga izpiše na stran brskalnika. Kodo nato zadostuje s klikom označiti in kopirati v projekt.

{control}

Najenostavnejši način, kako izrisati obrazec, je napisati v predlogi:

{control signInForm}

Vplivati na podobo tako izrisanega obrazca je mogoče s konfiguracijo Rendererja in posameznih elementov.

n:name

Definicijo obrazca v PHP kodi je mogoče izjemno enostavno povezati s HTML kodo. Zadostuje le dopolniti atribute n:name. Tako je enostavno!

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>

Podobo končne HTML kode imate popolnoma v svojih rokah. Če atribut n:name uporabite pri elementih <select>, <button> ali <textarea>, se njihova notranja vsebina samodejno dopolni. Oznaka <form n:name> poleg tega ustvari lokalno spremenljivko $form z objektom risanega obrazca in zaključna </form> izriše vse neizrisane skrite elemente (enako velja tudi za {form} ... {/form}).

Ne smemo pa pozabiti na izrisovanje morebitnih sporočil o napakah. In to tako tistih, ki so se z metodo addError() dodala k posameznim elementom (s pomočjo {inputError}), kot tudi tistih, dodanih neposredno k obrazcu (vrača jih $form->getOwnErrors()):

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

Bolj zapletene elemente obrazca, kot sta RadioList ali CheckboxList, je mogoče tako izrisovati po posameznih postavkah:

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

{label} {input}

Ne želite pri vsakem elementu razmišljati, kateri HTML element zanj uporabiti v predlogi, ali <input>, <textarea> itd.? Rešitev je univerzalna oznaka {input}:

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

Če obrazec uporablja prevajalnik|, bo besedilo znotraj oznak {label} prevedeno.

Tudi v tem primeru je mogoče bolj zapletene elemente obrazca, kot sta RadioList ali CheckboxList, izrisovati po posameznih postavkah:

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

Za izrisovanje samega <input> v elementu Checkbox uporabite {input myCheckbox:}. HTML atribute v tem primeru vedno ločujte z vejico {input myCheckbox:, class: required}.

{inputError}

Izpiše sporočilo o napaki k elementu obrazca, če ga ima. Sporočilo običajno zavijemo v HTML element zaradi stiliranja. Preprečiti izrisovanje praznega elementa, če sporočila ni, je mogoče elegantno s pomočjo n:ifcontent:

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

Prisotnost napake lahko ugotovimo z metodo hasErrors() in glede na to nastavimo razred nadrejenemu elementu:

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

{form}

Oznake {form signInForm}...{/form} so alternativa k <form n:name="signInForm">...</form>.

Samodejno izrisovanje

Zahvaljujoč oznakam {input} in {label} lahko enostavno ustvarimo splošno predlogo za kateri koli obrazec. Postopoma bo iterirala in izrisovala vse njegove elemente, razen skritih elementov, ki se izrišejo samodejno ob zaključku obrazca z oznako </form>. Ime izrisovanega obrazca bo pričakovala v spremenljivki $form.

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

Uporabljene samozaključujoče parne oznake {label .../} prikazujejo oznake, ki izhajajo iz definicije obrazca v PHP kodi.

To splošno predlogo shranite na primer v datoteko basic-form.latte in za izrisovanje obrazca jo zadostuje vključiti ter predati ime (ali instanco) obrazca v parameter $form:

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

Če bi pri izrisovanju enega določenega obrazca želeli poseči v njegovo podobo in na primer en element izrisati drugače, potem je najenostavnejša pot, da si v predlogi predpripravite bloke, ki jih bo mogoče kasneje prepisati. Bloki imajo lahko tudi dinamična imena, vanje je tako mogoče vstaviti tudi ime izrisovanega elementa. Na primer:

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

Za element npr. username tako nastane blok input-username, ki ga je mogoče enostavno prepisati z uporabo oznake {embed}:

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

Alternativno je mogoče celotno vsebino predloge basic-form.latte definirati kot blok, vključno s parametrom $form:

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

Zahvaljujoč temu bo njegov klic nekoliko enostavnejši:

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

Blok pri tem zadostuje uvoziti na enem samem mestu in to na začetku predloge postavitve:

{import basic-form.latte}

Posebni primeri

Če potrebujete izrisati le notranji del obrazca brez HTML oznak <form>, na primer pri pošiljanju odrezkov (snippetov), jih skrijte s pomočjo atributa n:tag-if:

<form n:name=signInForm n:tag-if=false>
	<div>
		<label n:name=username>Username: <input n:name=username></label>
		{inputError username}
	</div>
</form>

Z izrisovanjem elementov znotraj vsebnika obrazca pomaga oznaka {formContainer}.

<p>Katere novice želite prejemati:</p>

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

Izrisovanje brez Latte

Najenostavnejši način, kako izrisati obrazec, je poklicati:

$form->render();

Vplivati na podobo tako izrisanega obrazca je mogoče s konfiguracijo Rendererja in posameznih elementov.

Ročno izrisovanje

Vsak element obrazca ima metode, ki generirajo HTML kodo polja obrazca in oznake. Lahko jo vračajo bodisi kot niz ali objekt Nette\Utils\Html:

  • getControl(): Html|string vrne HTML kodo elementa
  • getLabel($caption = null): Html|string|null vrne HTML kodo oznake, če obstaja

Obrazec je tako mogoče izrisovati po posameznih elementih:

<?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') ?>

Medtem ko pri nekaterih elementih getControl() vrne en sam HTML element (npr. <input>, <select> ipd.), pri drugih cel kos HTML kode (CheckboxList, RadioList). V takem primeru lahko uporabite metode, ki generirajo posamezne vnose in oznake, za vsako postavko posebej:

  • getControlPart($key = null): ?Html vrne HTML kodo ene postavke
  • getLabelPart($key = null): ?Html vrne HTML kodo oznake ene postavke

Te metode imajo iz zgodovinskih razlogov predpono get, vendar bi bil boljši generate, ker pri vsakem klicu ustvari in vrne nov element Html.

Renderer

Gre za objekt, ki zagotavlja izrisovanje obrazca. Tega je mogoče nastaviti z metodo $form->setRenderer. Njemu se preda nadzor ob klicu metode $form->render().

Če ne nastavimo lastnega rendererja, bo uporabljen privzeti izrisovalnik Nette\Forms\Rendering\DefaultFormRenderer. Ta elemente obrazca izriše v obliki HTML tabele. Izhod izgleda takole:

<table>
<tr class="required">
	<th><label class="required" for="frm-name">Ime:</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">Starost:</label></th>

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

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

Ali uporabiti ali ne uporabiti za ogrodje obrazca tabelo je sporno in vrsta spletnih oblikovalcev preferira drugačen markup. Na primer definicijski seznam. Prekonfiguriramo zato DefaultFormRenderer tako, da obrazec izriše v obliki seznama. Konfiguracija se izvaja z urejanjem polja $wrappers. Prvi indeks vedno predstavlja območje in drugi njegov atribut. Posamezna območja ponazarja slika:

Standardno je skupina elementov controls ovita s tabelo <table>, vsak pair predstavlja vrstico tabele <tr> in par label ter control sta celici <th> in <td>. Zdaj ovojne elemente spremenimo. Območje controls vstavimo v vsebnik <dl>, območje pair pustimo brez vsebnika, label vstavimo v <dt> in na koncu control ovijemo z oznakami <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();

Rezultat je ta HTML koda:

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

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


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

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


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

V polju wrappers je mogoče vplivati na celo vrsto drugih atributov:

  • dodajati CSS razrede posameznim tipom elementov obrazca
  • razlikovati CSS razred lihih in sodih vrstic
  • vizualno ločiti obvezne in neobvezne postavke
  • določati, ali se sporočila o napakah prikažejo neposredno pri elementih ali nad obrazcem

Možnosti (Options)

Obnašanje Rendererja je mogoče nadzorovati tudi z nastavljanjem options na posameznih elementih obrazca. Tako je mogoče nastaviti napis, ki se izpiše poleg vnosnega polja:

$form->addText('phone', 'Številka:')
	->setOption('description', 'Ta številka bo ostala skrita');

Če vanj želimo umestiti HTML vsebino, uporabimo razred Html

use Nette\Utils\Html;

$form->addText('phone', 'Številka:')
	->setOption('description', Html::el('p')
		->setHtml('<a href="...">Pogoji shranjevanja Vaše številke</a>')
	);

Html element je mogoče uporabiti tudi namesto oznake: $form->addCheckbox('conditions', $label).

Združevanje elementov

Renderer omogoča združevanje elementov v vizualne skupine (fieldsete):

$form->addGroup('Osebni podatki');

Po ustvarjanju nove skupine ta postane aktivna in vsak na novo dodan element je hkrati dodan tudi vanjo. Tako je mogoče obrazec graditi na ta način:

$form = new Form;
$form->addGroup('Osebni podatki');
$form->addText('name', 'Vaše ime:');
$form->addInteger('age', 'Vaša starost:');
$form->addEmail('email', 'E-pošta:');

$form->addGroup('Naslov za dostavo');
$form->addCheckbox('send', 'Pošlji na naslov');
$form->addText('street', 'Ulica:');
$form->addText('city', 'Mesto:');
$form->addSelect('country', 'Država:', $countries);

Renderer najprej izrisuje skupine in šele nato elemente, ki ne pripadajo nobeni skupini.

Podpora za Bootstrap

V primerih najdete primere, kako konfigurirati Renderer za Twitter Bootstrap 2, Bootstrap 3 in Bootstrap 4

HTML atributi

Za nastavitev poljubnih HTML atributov elementov obrazca uporabimo metodo setHtmlAttribute(string $name, $value = true):

$form->addInteger('number', 'Število:')
	->setHtmlAttribute('class', 'big-number');

$form->addSelect('rank', 'Razvrsti po:', ['ceni', 'nazivu'])
	->setHtmlAttribute('onchange', 'submit()'); // ob spremembi pošlji


// Za nastavitev atributov samega <form>
$form->setHtmlAttribute('id', 'myForm');

Specifikacija tipa elementa:

$form->addText('tel', 'Vaš telefon:')
	->setHtmlType('tel')
	->setHtmlAttribute('placeholder', 'napišite telefon');

Nastavitev tipa in drugih atributov služi le za vizualne namene. Preverjanje pravilnosti vnosov mora potekati na strežniku, kar zagotovite z izbiro ustreznega elementa obrazca in navedbo validacijskih pravil.

Posameznim postavkam v seznamih izbirnih gumbov ali potrditvenih polj lahko nastavimo HTML atribut z različnimi vrednostmi za vsako od njih. Opazite dvopičje za style:, ki zagotovi izbiro vrednosti glede na ključ:

$colors = ['r' => 'rdeča', 'g' => 'zelena', 'b' => 'modra'];
$styles = ['r' => 'background:red', 'g' => 'background:green'];
$form->addCheckboxList('colors', 'Barve:', $colors)
	->setHtmlAttribute('style:', $styles);

Izpiše:

<label><input type="checkbox" name="colors[]" style="background:red" value="r">rdeča</label>
<label><input type="checkbox" name="colors[]" style="background:green" value="g">zelena</label>
<label><input type="checkbox" name="colors[]" value="b">modra</label>

Za nastavitev logičnih atributov, kot je readonly, lahko uporabimo zapis z vprašajem:

$form->addCheckboxList('colors', 'Barve:', $colors)
	->setHtmlAttribute('readonly?', 'r'); // za več ključev uporabite polje, npr. ['r', 'g']

Izpiše:

<label><input type="checkbox" name="colors[]" readonly value="r">rdeča</label>
<label><input type="checkbox" name="colors[]" value="g">zelena</label>
<label><input type="checkbox" name="colors[]" value="b">modra</label>

V primeru izbirnih polj (selectbox) metoda setHtmlAttribute() nastavlja atribute elementa <select>. Če želimo nastaviti atribute posameznim <option>, uporabimo metodo setOptionAttribute(). Delujejo tudi zapisi z dvopičjem in vprašajem, navedeni zgoraj:

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

Izpiše:

<select name="colors">
	<option value="r" style="background:red">rdeča</option>
	<option value="g" style="background:green">zelena</option>
	<option value="b">modra</option>
</select>

Prototipi

Alternativni način nastavljanja HTML atributov temelji na urejanju predloge, iz katere se HTML element generira. Predloga je objekt Html in jo vrača metoda getControlPrototype():

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

Na ta način je mogoče modificirati tudi predlogo oznake, ki jo vrača getLabelPrototype():

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

Pri elementih Checkbox, CheckboxList in RadioList lahko vplivate na predlogo elementa, ki celoten element ovija. Vrača jo getContainerPrototype(). V privzetem stanju gre za „prazen“ element, tako da se nič ne izrisuje, a s tem, da mu nastavimo ime, se bo izrisoval:

$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>

V primeru CheckboxList in RadioList lahko vplivate tudi na predlogo ločila posameznih postavk, ki ga vrača metoda getSeparatorPrototype(). V privzetem stanju je to element <br>. Če ga spremenite v parni element, bo posamezne postavke ovijal namesto ločeval. In dalje lahko vplivate na predlogo HTML elementa oznake pri posameznih postavkah, ki ga vrača getItemLabelPrototype().

Prevajanje

Če programirate večjezično aplikacijo, boste verjetno potrebovali obrazec izrisati v različnih jezikovnih mutacijah. Nette Framework za ta namen definira vmesnik za prevajanje Nette\Localization\Translator. V Nette ni nobene privzete implementacije, lahko si izberete glede na svoje potrebe iz več pripravljenih rešitev, ki jih najdete na Componette. V njihovi dokumentaciji boste izvedeli, kako prevajalnik konfigurirati.

Obrazci podpirajo izpisovanje besedil preko prevajalnika. Predamo jim ga s pomočjo metode setTranslator():

$form->setTranslator($translator);

Od te točke naprej se ne le vse oznake, ampak tudi vsa sporočila o napakah ali postavke izbirnih polj prevedejo v drug jezik.

Pri posameznih elementih obrazca je pri tem mogoče nastaviti drug prevajalnik ali prevajanje popolnoma izklopiti z vrednostjo null:

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

Pri validacijskih pravilih se prevajalniku predajajo tudi specifični parametri, na primer pri pravilu:

$form->addPassword('password', 'Geslo:')
	->addRule($form::MinLength, 'Geslo mora imeti vsaj %d znakov', 8);

se kliče prevajalnik s temi parametri:

$translator->translate('Geslo mora imeti vsaj %d znakov', 8);

in torej lahko izbere pravilno obliko množine pri besedi znakov glede na število.

Dogodek onRender

Tik preden se obrazec izriše, lahko pustimo poklicati našo kodo. Ta lahko na primer dopolni elementom obrazca HTML razrede za pravilno prikazovanje. Kodo dodamo v polje onRender:

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