Form Oluşturma

Formların görünümü çok çeşitli olabilir. Uygulamada iki uç noktayla karşılaşabiliriz. Bir yandan, bir uygulamada görsel olarak birbirine benzeyen bir dizi form oluşturma ihtiyacı vardır ve $form->render() kullanarak şablon olmadan kolay oluşturmayı takdir ederiz. Bu durum genellikle yönetim arayüzleri için geçerlidir.

Öte yandan, her birinin benzersiz olduğu çeşitli formlar vardır. Bunların görünümü en iyi şablonda HTML dili kullanılarak tanımlanır. Ve elbette, bahsedilen her iki aşırı uca ek olarak, arada bir yere düşen birçok formla karşılaşacağız.

Latte ile Rendering

Latte şablonlama sistemi, formların ve öğelerinin oluşturulmasını temel olarak kolaylaştırır. İlk olarak, kod üzerinde tam kontrol elde etmek için formların öğe öğe manuel olarak nasıl oluşturulacağını göstereceğiz. Daha sonra bu işlemin nasıl otomatikleştirileceğini göstereceğiz.

Nette\Forms\Blueprint::latte($form) yöntemini kullanarak oluşturulan form için bir Latte şablonu önerisine sahip olabilirsiniz, bu da tarayıcı sayfasına çıktı verecektir. Ardından, kodu bir tıklama ile seçmeniz ve projenize kopyalamanız yeterlidir.

{control}

Bir formu oluşturmanın en kolay yolu bir şablon içine yazmaktır:

{control signInForm}

Oluşturulan formun görünümü, Renderer ve bireysel kontroller yapılandırılarak değiştirilebilir.

n:name

PHP kodundaki form tanımını HTML koduna bağlamak son derece kolaydır. Sadece n:name niteliklerini ekleyin. İşte bu kadar kolay!

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>

Ortaya çıkan HTML kodunun görünümü tamamen sizin elinizdedir. n:name özniteliğini şu şekilde kullanırsanız <select>, <button> veya <textarea> öğeleri varsa, iç içerikleri otomatik olarak doldurulur. Buna ek olarak <form n:name> etiketi, çizilen form nesnesi ve kapanış ile $form yerel değişkenini oluşturur. </form> çizilmemiş tüm gizli öğeleri çizer (aynı durum {form} ... {/form} için de geçerlidir).

Ancak, olası hata mesajlarını oluşturmayı unutmamalıyız. Hem addError() yöntemi ( {inputError} kullanılarak) tarafından tek tek öğelere eklenenler hem de doğrudan forma eklenenler ( $form->getOwnErrors() tarafından döndürülenler):

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

RadioList veya CheckboxList gibi daha karmaşık form öğeleri öğe öğe işlenebilir:

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

{label} {input}

Her bir öğe için şablonda hangi HTML öğesini kullanacağınızı düşünmek istemez misiniz? <input>, <textarea> vb. Çözüm evrensel {input} etiketidir:

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

Form bir çevirmen kullanıyorsa, {label} etiketlerinin içindeki metin çevrilecektir.

Yine, RadioList veya CheckboxList gibi daha karmaşık form öğeleri öğe öğe işlenebilir:

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

İşlemek için <input> 'nin kendisini Onay Kutusu öğesinde kullanmak için {input myCheckbox:} adresini kullanın. HTML nitelikleri virgülle ayrılmalıdır {input myCheckbox:, class: required}.

{inputError}

Form öğesi için, eğer varsa, bir hata mesajı yazdırır. Mesaj genellikle stil için bir HTML öğesine sarılır. Mesaj yoksa boş bir öğe oluşturmaktan kaçınmak n:ifcontent ile zarif bir şekilde yapılabilir:

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

hasErrors() yöntemini kullanarak bir hatanın varlığını tespit edebilir ve üst öğenin sınıfını buna göre ayarlayabiliriz:

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

{form}

Etiketler {form signInForm}...{/form} bir alternatiftir <form n:name="signInForm">...</form>.

Otomatik Rendering

{input} ve {label} etiketleri ile herhangi bir form için kolayca genel bir şablon oluşturabiliriz. Form şu etiketle sonlandırıldığında otomatik olarak işlenen gizli öğeler hariç, tüm öğelerini sırayla yineleyecek ve işleyecektir </form> etiketini kullanır. Oluşturulan formun adını $form değişkeninde bekleyecektir.

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

Kullanılan kendi kendine kapanan çift etiketleri {label .../} PHP kodundaki form tanımından gelen etiketleri gösterir.

Bu genel şablonu basic-form.latte dosyasına kaydedebilir ve formu oluşturmak için onu dahil edip form adını (veya örneğini) $form parametresine aktarabilirsiniz:

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

Belirli bir formun görünümünü etkilemek ve bir öğeyi farklı şekilde çizmek istiyorsanız, bunun en kolay yolu şablonda daha sonra üzerine yazılabilecek bloklar hazırlamaktır. Bloklar dinamik isimlere de sahip olabilir, böylece içine çizilecek öğenin adını ekleyebilirsiniz. Örneğin:

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

Örneğin username öğesi için bu, {embed} etiketi kullanılarak kolayca geçersiz kılınabilecek input-username bloğunu oluşturur:

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

Alternatif olarak, basic-form.latte şablonunun tüm içeriği $form parametresi de dahil olmak üzere bir blok olarak tanımlanabilir:

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

Bu, kullanımı biraz daha kolaylaştıracaktır:

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

Bloğu yalnızca bir yerde, düzen şablonunun başında içe aktarmanız gerekir:

{import basic-form.latte}

Özel Durumlar

Formun yalnızca iç kısmını HTML etiketleri olmadan oluşturmanız gerekiyorsa <form>örneğin snippet gönderirken, n:tag-if özniteliğini kullanarak bunları gizleyin:

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

Etiket formContainer bir form konteyneri içindeki girdilerin oluşturulmasına yardımcı olur.

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

Bir formu oluşturmanın en kolay yolu çağırmaktır:

$form->render();

Oluşturulan formun görünümü, Renderer ve bireysel kontroller yapılandırılarak değiştirilebilir.

Manuel Rendering

Her form öğesi, form alanı ve etiket için HTML kodu üreten yöntemlere sahiptir. Bu kodlar bir dize ya da Nette\Utils\Html nesnesi olarak döndürülebilir:

  • getControl(): Html|string öğenin HTML kodunu döndürür
  • getLabel($caption = null): Html|string|null varsa etiketin HTML kodunu döndürür

Bu, formun öğe öğe işlenmesini sağlar:

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

Bazı öğeler için getControl() tek bir HTML öğesi döndürürken (örn. <input>, <select> vb.), diğerleri için bütün bir HTML kodu parçası döndürür (CheckboxList, RadioList). Bu durumda, her öğe için ayrı ayrı girdiler ve etiketler oluşturan yöntemler kullanabilirsiniz:

  • getControlPart($key = null): ?Html tek bir öğenin HTML kodunu döndürür
  • getLabelPart($key = null): ?Html tek bir öğenin etiketi için HTML kodunu döndürür

Bu yöntemlerin önüne tarihsel nedenlerden dolayı get eklenmiştir, ancak her çağrıda yeni bir Html öğesi oluşturup döndürdüğü için generate daha iyi olacaktır.

Oluşturucu

Formun işlenmesini sağlayan bir nesnedir. $form->setRenderer metodu tarafından ayarlanabilir. $form->render() metodu çağrıldığında kontrol aktarılır.

Eğer özel bir renderer ayarlamazsak, varsayılan renderer Nette\Forms\Rendering\DefaultFormRenderer kullanılacaktır. Bu, form öğelerini bir HTML tablosu olarak oluşturacaktır. Çıktı şu şekilde görünür:

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

Tablo kullanıp kullanmamak size bağlıdır ve birçok web tasarımcısı farklı işaretlemeleri tercih eder, örneğin bir liste. DefaultFormRenderer adresini hiç tabloya dönüştürmeyecek şekilde yapılandırabiliriz. Sadece uygun $wrappers ayarlamamız gerekiyor. İlk indeks her zaman bir alanı, ikincisi ise bir elementi temsil eder. İlgili tüm alanlar resimde gösterilmiştir:

Varsayılan olarak bir controls grubu <table>ve her pair bir tablo satırıdır <tr> bir çift label ve control içeren (hücreler <th> ve <td>). Tüm bu sarmalayıcı öğeleri değiştirelim. controls adresini <dl> pair adresini tek başına bırakın, label adresini <dt> ve control adresini <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();

Sonuçlar aşağıdaki kod parçacığına dönüşür:

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

Sarmalayıcılar birçok özelliği etkileyebilir. Örneğin:

  • her form girişine özel CSS sınıfları ekleyin
  • tek ve çift çizgiler arasında ayrım yapabilme
  • gerekli ve isteğe bağlı çizimleri farklı yapın
  • hata mesajlarının formun üstünde mi yoksa her bir öğenin yakınında mı gösterileceğini belirler

Seçenekler

Renderer'ın davranışı, tek tek form öğeleri üzerinde seçenekler ayarlanarak da kontrol edilebilir. Bu şekilde, giriş alanının yanında görüntülenen araç ipucunu ayarlayabilirsiniz:

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

Eğer içine HTML içeriği yerleştirmek istiyorsak Html sınıfını kullanırız.

use Nette\Utils\Html;

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

Etiket yerine Html öğesi de kullanılabilir: $form->addCheckbox('conditions', $label).

Gruplama Girişleri

Renderer, öğeleri görsel gruplar (alan kümeleri) halinde gruplandırmaya izin verir:

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

Yeni grup oluşturmak onu etkinleştirir – daha sonra eklenen tüm öğeler bu gruba eklenir. Bunun gibi bir form oluşturabilirsiniz:

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

Oluşturucu önce grupları, ardından da herhangi bir gruba ait olmayan öğeleri çizer.

Bootstrap Desteği

Twitter Bootstrap 2, Bootstrap 3 ve Bootstrap 4 için Renderer yapılandırma örneklerini bulabilirsiniz

HTML Nitelikleri

setHtmlAttribute(string $name, $value = true) adresini kullanarak form denetimlerine herhangi bir HTML niteliği atayabilirsiniz:

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

$form->addSelect('rank', 'Order by:', ['price', 'name'])
	->setHtmlAttribute('onchange', 'submit()'); // değişiklik üzerine JS fonksiyonu submit()'i çağırır


// <form> üzerinde uygulama
$form->setHtmlAttribute('id', 'myForm');

Giriş türünü ayarlama:

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

HTML niteliğini radyo veya onay kutusu listelerindeki her bir öğe için farklı değerlerle ayarlayabiliriz. Değerin anahtara göre seçildiğinden emin olmak için style: adresinden sonra iki nokta üst üste işaretine dikkat edin:

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

Render:

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

Mantıksal bir HTML niteliği için (değeri olmayan, örneğin readonly), bir soru işareti kullanabilirsiniz:

$colors = ['r' => 'kırmızı', 'g' => 'yeşil', 'b' => 'mavi'];
$form->addCheckboxList('colors', 'Colors:', $colors)
	->setHtmlAttribute('readonly?', 'r'); // birden fazla anahtar için dizi kullanın, örneğin ['r', 'g']

Render:

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

Selectbox'lar için setHtmlAttribute() yöntemi, selectbox'ın niteliklerini ayarlar. <select> öğesi. Her bir eleman için öznitelikleri ayarlamak istersek <option>yöntemini kullanacağız setOptionAttribute(). Ayrıca, yukarıda kullanılan iki nokta üst üste ve soru işareti de çalışır:

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

Render:

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

Prototipler

HTML niteliklerini ayarlamanın alternatif bir yolu, HTML öğesinin oluşturulduğu şablonu değiştirmektir. Şablon bir Html nesnesidir ve getControlPrototype() yöntemi tarafından döndürülür:

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

getLabelPrototype() tarafından döndürülen etiket şablonu da bu şekilde değiştirilebilir:

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

Checkbox, CheckboxList ve RadioList öğeleri için öğeyi saran öğe şablonunu etkileyebilirsiniz. getContainerPrototype() tarafından döndürülür. Varsayılan olarak “boş” bir öğedir, bu nedenle hiçbir şey işlenmez, ancak bir ad vererek işlenecektir:

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

CheckboxList ve RadioList söz konusu olduğunda, getSeparatorPrototype() yöntemi tarafından döndürülen öğe ayırıcı modelini etkilemek de mümkündür. Varsayılan olarak, bu bir öğedir <br>. Bunu bir çift öğe olarak değiştirirseniz, öğeleri ayırmak yerine tek tek saracaktır. Ayrıca, öğe etiketlerinin HTML öğesi şablonunu etkilemek de mümkündür; bu şablon getItemLabelPrototype() döndürür.

Çeviri

Çok dilli bir uygulama programlıyorsanız, muhtemelen formu farklı dillerde oluşturmanız gerekecektir. Nette Framework bu amaç için bir çeviri arayüzü tanımlar Nette\Localization\Translator. Nette'de varsayılan bir uygulama yoktur, Componette'de bulabileceğiniz birkaç hazır çözüm arasından ihtiyaçlarınıza göre seçim yapabilirsiniz. Belgeleri size çevirmeni nasıl yapılandıracağınızı anlatır.

Form, çevirmen aracılığıyla metin çıktısı almayı destekler. Bunu setTranslator() yöntemini kullanarak aktarıyoruz:

$form->setTranslator($translator);

Şu andan itibaren, sadece tüm etiketler değil, aynı zamanda tüm hata mesajları veya seçim kutusu girişleri de başka bir dile çevrilecektir.

Tek tek form öğeleri için farklı bir çevirmen ayarlamak veya null ile çeviriyi tamamen devre dışı bırakmak mümkündür:

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

Doğrulama kuralları için, örneğin kural gibi belirli parametreler de çevirmene aktarılır:

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

çevirmen aşağıdaki parametrelerle çağrılır:

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

ve böylece characters sözcüğü için doğru çoğul biçimini sayarak seçebilir.

Olay onRender

Form işlenmeden hemen önce kodumuzu çağırabiliriz. Bu, örneğin, düzgün görüntüleme için form öğelerine HTML sınıfları ekleyebilir. Kodu onRender dizisine ekliyoruz:

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