Formların Render Edilmesi
Formların görünümü çok çeşitli olabilir. Pratikte iki uç durumla karşılaşabiliriz. Bir yanda, uygulamada görsel
olarak birbirine çok benzeyen bir dizi formu render etme ihtiyacı vardır ve $form->render()
kullanarak şablon
olmadan kolay render etmeyi takdir ederiz. Bu genellikle yönetim arayüzleri durumudur.
Diğer yanda, her birinin orijinal olduğu çeşitli formlar vardır. Görünümleri en iyi HTML diliyle form şablonunda tanımlanır. Ve elbette, bahsedilen her iki uç durumun yanı sıra, arada bir yerde bulunan birçok formla karşılaşacağız.
Latte ile Render Etme
Latte şablonlama sistemi formların ve elemanlarının render edilmesini önemli ölçüde kolaylaştırır. Önce formları manuel olarak tek tek elemanlarla nasıl render edeceğimizi ve böylece kod üzerinde tam kontrol sahibi olacağımızı göstereceğiz. Daha sonra böyle bir render işlemini nasıl otomatikleştirebileceğinizi göstereceğiz.
Formun Latte şablonu tasarımını Nette\Forms\Blueprint::latte($form)
metodunu kullanarak
oluşturabilirsiniz, bu da onu tarayıcı sayfasına yazdırır. Kodu daha sonra tıklayarak işaretleyip projeye kopyalamak
yeterlidir.
{control}
Formu render etmenin en basit yolu şablonda şunu yazmaktır:
{control signInForm}
Bu şekilde render edilen formun görünümünü Renderer ve bireysel elemanlar yapılandırarak etkileyebilirsiniz.
n:name
PHP kodundaki form tanımını HTML koduyla ilişkilendirmek son derece kolaydır. Sadece n:name
niteliklerini
eklemek yeterlidir. 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>Kullanıcı adı: <input n:name=username size=20 autofocus></label>
</div>
<div>
<label n:name=password>Şifre: <input n:name=password></label>
</div>
<div>
<input n:name=send class="btn btn-default">
</div>
</form>
Sonuçtaki HTML kodunun görünümü tamamen sizin kontrolünüzdedir. n:name
niteliğini
<select>
, <button>
veya <textarea>
elemanlarında kullanırsanız, iç
içerikleri otomatik olarak tamamlanır. <form n:name>
etiketi ayrıca render edilen formun nesnesiyle
$form
yerel değişkenini oluşturur ve kapanış </form>
etiketi render edilmemiş tüm gizli
elemanları render eder (aynısı {form} ... {/form}
için de geçerlidir).
Ancak olası hata mesajlarının render edilmesini unutmamalıyız. Hem addError()
metoduyla bireysel elemanlara
eklenenler ( {inputError}
kullanarak) 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>Kullanıcı adı: <input n:name=username size=20 autofocus></label>
<span class=error n:ifcontent>{inputError username}</span>
</div>
<div>
<label n:name=password>Şifre: <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 elemanları bu şekilde tek tek öğelerle render edilebilir:
{foreach $form[gender]->getItems() as $key => $label}
<label n:name="gender:$key"><input n:name="gender:$key"> {$label}</label>
{/foreach}
{label}
{input}
Her eleman için şablonda hangi HTML elemanını kullanacağınızı düşünmek istemiyor musunuz,
<input>
, <textarea>
vb. mi? Çö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}Kullanıcı adı: {input username, size: 20, autofocus: true}{/label}
{inputError username}
</div>
<div>
{label password}Şifre: {input password}{/label}
{inputError password}
</div>
<div>
{input send, class: "btn btn-default"}
</div>
</form>
Form bir çevirmen kullanıyorsa, {label}
etiketleri içindeki metin çevrilecektir.
Bu durumda bile, RadioList veya CheckboxList gibi daha karmaşık form elemanları tek tek öğelerle render edilebilir:
{foreach $form[gender]->items as $key => $label}
{label gender:$key}{input gender:$key} {$label}{/label}
{/foreach}
Checkbox elemanındaki <input>
'ın kendisini render etmek için {input myCheckbox:}
kullanın.
Bu durumda HTML niteliklerini her zaman virgülle ayırın {input myCheckbox:, class: required}
.
{inputError}
Bir form elemanı için varsa hata mesajını yazdırır. Mesajı genellikle stil vermek için bir HTML elemanına sararız.
Mesaj yoksa boş bir elemanın render edilmesini önlemek, n:ifcontent
kullanarak zarif bir şekilde
yapılabilir:
<span class=error n:ifcontent>{inputError $input}</span>
Bir hatanın varlığını hasErrors()
metoduyla kontrol edebilir ve buna göre üst elemana bir sınıf
atayabiliriz:
<div n:class="$form[username]->hasErrors() ? 'error'">
{input username}
{inputError username}
</div>
{form}
{form signInForm}...{/form}
etiketleri <form n:name="signInForm">...</form>
'a bir
alternatiftir.
Otomatik Render Etme
{input}
ve {label}
etiketleri sayesinde, herhangi bir form için kolayca genel bir şablon
oluşturabiliriz. Sırayla tüm elemanlarını yineleyecek ve render edecektir, formun </form>
etiketiyle
sonlandırıldığında otomatik olarak render edilen gizli elemanlar hariç. Render edilen formun adı $form
değişkeninde beklenecektir.
<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 etiketler {label .../}
PHP kodundaki form tanımından gelen etiketleri
görüntüler.
Bu genel şablonu örneğin basic-form.latte
dosyasına kaydedin ve formu render etmek için onu dahil etmek ve
formun adını (veya örneğini) $form
parametresine iletmek yeterlidir:
{include basic-form.latte, form: signInForm}
Belirli bir formu render ederken görünümüne müdahale etmek ve örneğin bir elemanı farklı render etmek isterseniz, en kolay yol şablonda daha sonra üzerine yazılabilecek bloklar hazırlamaktır. Bloklar ayrıca dinamik isimler sahip olabilir, bu nedenle render edilen elemanın adını da içlerine ekleyebilirsiniz. Örneğin:
...
{label $input /}
{block "input-{$input->name}"}{input $input}{/block}
...
Örneğin username
elemanı için, {embed} etiketi kullanılarak kolayca üzerine
yazılabilecek input-username
bloğu oluşturulur:
{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 dahil olmak üzere
bir blok olarak tanımlanabilir:
{define basic-form, $form}
<form n:name=$form class=form>
...
</form>
{/define}
Bu sayede çağrısı biraz daha basit olacaktır:
{embed basic-form, signInForm}
...
{/embed}
Bloğu yalnızca tek bir yerde, layout şablonunun başında içe aktarmak yeterlidir:
{import basic-form.latte}
Özel Durumlar
Formun yalnızca iç kısmını HTML <form>
etiketleri olmadan render etmeniz gerekiyorsa, örneğin snippet
gönderirken, bunları n:tag-if
niteliğiyle gizleyin:
<form n:name=signInForm n:tag-if=false>
<div>
<label n:name=username>Kullanıcı adı: <input n:name=username></label>
{inputError username}
</div>
</form>
Form konteyneri içindeki elemanların render edilmesine {formContainer}
etiketi yardımcı olur.
<p>Hangi haberleri almak istersiniz:</p>
{formContainer emailNews}
<ul>
<li>{input sport} {label sport /}</li>
<li>{input science} {label science /}</li>
</ul>
{/formContainer}
Latte Olmadan Render Etme
Formu render etmenin en basit yolu çağırmaktır:
$form->render();
Bu şekilde render edilen formun görünümünü Renderer ve bireysel elemanlar yapılandırarak etkileyebilirsiniz.
Manuel Render Etme
Her form elemanı, form alanının ve etiketin HTML kodunu üreten metotlara sahiptir. Bunu ya bir dize olarak ya da bir Nette\Utils\Html nesnesi olarak döndürebilirler:
getControl(): Html|string
elemanın HTML kodunu döndürürgetLabel($caption = null): Html|string|null
varsa etiketin HTML kodunu döndürür
Form bu nedenle tek tek elemanlarla render edilebilir:
<?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ı elemanlar için getControl()
tek bir HTML elemanı (örneğin <input>
,
<select>
vb.) döndürürken, diğerleri için tüm bir HTML kod parçasını (CheckboxList, RadioList)
döndürür. Bu durumda, her öğe için ayrı ayrı tek tek girdileri ve etiketleri üreten metotları kullanabilirsiniz:
getControlPart($key = null): ?Html
bir öğenin HTML kodunu döndürürgetLabelPart($key = null): ?Html
bir öğenin etiketinin HTML kodunu döndürür
Bu metotların tarihsel nedenlerden dolayı get
öneki vardır, ancak her çağrıda yeni bir
Html
elemanı oluşturup döndürdükleri için generate
daha iyi olurdu.
Renderer
Formun render edilmesini sağlayan bir nesnedir. $form->setRenderer
metoduyla ayarlanabilir.
$form->render()
metodu çağrıldığında kontrol ona devredilir.
Kendi renderer'ımızı ayarlamazsak, varsayılan Nette\Forms\Rendering\DefaultFormRenderer render edicisi kullanılacaktır. Bu, form elemanlarını bir HTML tablosu şeklinde render eder. Çıktı şöyle görünür:
<table>
<tr class="required">
<th><label class="required" for="frm-name">İsim:</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">Yaş:</label></th>
<td><input type="text" class="text" name="age" id="frm-age" required value=""></td>
</tr>
<tr>
<th><label>Cinsiyet:</label></th>
...
Form iskeleti için tablo kullanıp kullanmamak tartışmalıdır ve birçok web tasarımcısı farklı bir işaretleme tercih
eder. Örneğin, bir tanım listesi. Bu nedenle DefaultFormRenderer
'ı formu bir liste şeklinde render edecek
şekilde yeniden yapılandıracağız. Yapılandırma $wrappers dizisini
düzenleyerek yapılır. İlk dizin her zaman alanı ve ikincisi onun niteliğini temsil eder. Bireysel alanlar resimde
gösterilmiştir:

Standart olarak, controls
eleman grubu bir tablo <table>
ile sarılır, her pair
bir tablo satırını <tr>
temsil eder ve label
ve control
çifti hücreler
<th>
ve <td>
'dir. Şimdi saran elemanları değiştireceğiz. controls
alanını <dl>
konteynerine koyacağız, pair
alanını konteynersiz bırakacağız,
label
'ı <dt>
'ye koyacağız ve son olarak control
'ü <dd>
etiketleriyle saracağız:
$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ç bu HTML kodudur:
<dl>
<dt><label class="required" for="frm-name">İsim:</label></dt>
<dd><input type="text" class="text" name="name" id="frm-name" required value=""></dd>
<dt><label class="required" for="frm-age">Yaş:</label></dt>
<dd><input type="text" class="text" name="age" id="frm-age" required value=""></dd>
<dt><label>Cinsiyet:</label></dt>
...
</dl>
Wrappers dizisinde diğer birçok niteliği etkileyebilirsiniz:
- bireysel form elemanı türlerine CSS sınıfları eklemek
- tek ve çift satırları CSS sınıfıyla ayırt etmek
- zorunlu ve isteğe bağlı öğeleri görsel olarak ayırt etmek
- hata mesajlarının doğrudan elemanların yanında mı yoksa formun üzerinde mi görüntüleneceğini belirlemek
Options
Renderer'ın davranışı, bireysel form elemanlarında options ayarlayarak da kontrol edilebilir. Bu şekilde, giriş alanının yanında yazdırılacak açıklamayı ayarlayabilirsiniz:
$form->addText('phone', 'Numara:')
->setOption('description', 'Bu numara gizli kalacaktır');
İçine HTML içeriği yerleştirmek istiyorsak, Html sınıfını kullanırız
use Nette\Utils\Html;
$form->addText('phone', 'Numara:')
->setOption('description', Html::el('p')
->setHtml('<a href="...">Numaranızın saklanma koşulları</a>')
);
Html elemanı etiket yerine de kullanılabilir: $form->addCheckbox('conditions', $label)
.
Elemanların Gruplandırılması
Renderer, elemanları görsel gruplara (fieldset) ayırmayı sağlar:
$form->addGroup('Kişisel Veriler');
Yeni bir grup oluşturulduktan sonra, bu grup aktif hale gelir ve yeni eklenen her eleman aynı zamanda ona eklenir. Yani formu bu şekilde oluşturabilirsiniz:
$form = new Form;
$form->addGroup('Kişisel Veriler');
$form->addText('name', 'Adınız:');
$form->addInteger('age', 'Yaşınız:');
$form->addEmail('email', 'E-posta:');
$form->addGroup('Teslimat Adresi');
$form->addCheckbox('send', 'Adrese gönder');
$form->addText('street', 'Sokak:');
$form->addText('city', 'Şehir:');
$form->addSelect('country', 'Ülke:', $countries);
Renderer önce grupları ve ancak ondan sonra hiçbir gruba ait olmayan elemanları render eder.
Bootstrap Desteği
Örneklerde Renderer'ı Twitter Bootstrap 2, Bootstrap 3 ve Bootstrap 4 için nasıl yapılandıracağınıza dair örnekler bulacaksınız.
HTML Nitelikleri
Form elemanlarının herhangi bir HTML niteliğini ayarlamak için setHtmlAttribute(string $name, $value = true)
metodunu kullanırız:
$form->addInteger('number', 'Numara:')
->setHtmlAttribute('class', 'big-number');
$form->addSelect('rank', 'Sıralama ölçütü:', ['fiyat', 'isim'])
->setHtmlAttribute('onchange', 'submit()'); // değişiklikte gönder
// <form> öğesinin kendisinin niteliklerini ayarlamak için
$form->setHtmlAttribute('id', 'myForm');
Eleman türünün belirtilmesi:
$form->addText('tel', 'Telefonunuz:')
->setHtmlType('tel')
->setHtmlAttribute('placeholder', 'telefonu yazın');
Türün ve diğer niteliklerin ayarlanması yalnızca görsel amaçlıdır. Girdilerin doğruluğunun doğrulanması sunucu tarafında yapılmalıdır, bu da uygun bir form elemanı seçerek ve doğrulama kuralları belirterek sağlanır.
Radyo veya onay kutusu listelerindeki bireysel öğelere, her biri için farklı değerlere sahip HTML niteliği atayabiliriz.
Anahtara göre değer seçimini sağlayan style:
sonrasındaki iki noktaya dikkat edin:
$colors = ['r' => 'kırmızı', 'g' => 'yeşil', 'b' => 'mavi'];
$styles = ['r' => 'background:red', 'g' => 'background:green'];
$form->addCheckboxList('colors', 'Renkler:', $colors)
->setHtmlAttribute('style:', $styles);
Yazdırır:
<label><input type="checkbox" name="colors[]" style="background:red" value="r">kırmızı</label>
<label><input type="checkbox" name="colors[]" style="background:green" value="g">yeşil</label>
<label><input type="checkbox" name="colors[]" value="b">mavi</label>
readonly
gibi mantıksal nitelikleri ayarlamak için soru işaretiyle yazımı kullanabiliriz:
$form->addCheckboxList('colors', 'Renkler:', $colors)
->setHtmlAttribute('readonly?', 'r'); // daha fazla anahtar için bir dizi kullanın, örn. ['r', 'g']
Yazdırır:
<label><input type="checkbox" name="colors[]" readonly value="r">kırmızı</label>
<label><input type="checkbox" name="colors[]" value="g">yeşil</label>
<label><input type="checkbox" name="colors[]" value="b">mavi</label>
Seçme kutuları (selectbox) durumunda, setHtmlAttribute()
metodu <select>
elemanının
niteliklerini ayarlar. Bireysel <option>
'ların niteliklerini ayarlamak istiyorsak,
setOptionAttribute()
metodunu kullanırız. Yukarıda belirtilen iki nokta ve soru işaretiyle yazımlar da
çalışır:
$form->addSelect('colors', 'Renkler:', $colors)
->setOptionAttribute('style:', $styles);
Yazdırır:
<select name="colors">
<option value="r" style="background:red">kırmızı</option>
<option value="g" style="background:green">yeşil</option>
<option value="b">mavi</option>
</select>
Prototypler
HTML niteliklerini ayarlamanın alternatif bir yolu, HTML elemanının üretildiği şablonu düzenlemektir. Şablon bir
Html
nesnesidir ve getControlPrototype()
metodu tarafından döndürülür:
$input = $form->addInteger('number', 'Numara:');
$html = $input->getControlPrototype(); // <input>
$html->class('big-number'); // <input class="big-number">
Bu şekilde, getLabelPrototype()
tarafından döndürülen etiket şablonunu da değiştirebilirsiniz:
$html = $input->getLabelPrototype(); // <label>
$html->class('distinctive'); // <label class="distinctive">
Checkbox, CheckboxList ve RadioList elemanlarında, tüm elemanı saran elemanın şablonunu etkileyebilirsiniz. Bu,
getContainerPrototype()
tarafından döndürülür. Varsayılan durumda bu “boş” bir elemandır, bu yüzden
hiçbir şey render edilmez, ancak ona bir ad atayarak render edilecektir:
$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>
CheckboxList ve RadioList durumunda, getSeparatorPrototype()
tarafından döndürülen bireysel öğelerin
ayırıcısının şablonunu da etkileyebilirsiniz. Varsayılan durumda bu <br>
elemanıdır. Onu çiftli bir
elemana değiştirirseniz, bireysel öğeleri ayırmak yerine saracaktır. Ayrıca, getItemLabelPrototype()
tarafından döndürülen bireysel öğelerdeki etiket HTML elemanının şablonunu da etkileyebilirsiniz.
Çeviri
Çok dilli bir uygulama programlıyorsanız, muhtemelen formu farklı dil mutasyonlarında render etmeniz gerekecektir. Nette Framework bu amaçla çeviri için Nette\Localization\Translator arayüzünü tanımlar. Nette'de varsayılan bir uygulama yoktur, ihtiyaçlarınıza göre Componette üzerinde bulabileceğiniz birkaç hazır çözüm arasından seçim yapabilirsiniz. Belgelerinde çevirmeni nasıl yapılandıracağınızı öğreneceksiniz.
Formlar, metinlerin bir çevirmen aracılığıyla yazdırılmasını destekler. Onu setTranslator()
metoduyla
iletiriz:
$form->setTranslator($translator);
Bu andan itibaren, yalnızca tüm etiketler değil, aynı zamanda tüm hata mesajları veya seçme kutusu (select box) öğeleri de başka bir dile çevrilecektir.
Bireysel form elemanları için farklı bir çevirmen ayarlamak veya null
değeriyle çeviriyi tamamen kapatmak
mümkündür:
$form->addSelect('carModel', 'Model:', $cars)
->setTranslator(null);
Doğrulama kuralları için çevirmene özel parametreler de iletilir, örneğin kural için:
$form->addPassword('password', 'Şifre:')
->addRule($form::MinLength, 'Şifre en az %d karakter olmalıdır', 8);
çevirmen şu parametrelerle çağrılır:
$translator->translate('Şifre en az %d karakter olmalıdır', 8);
ve dolayısıyla sayıya göre karakter
kelimesinin doğru çoğul biçimini seçebilir.
onRender Olayı
Form render edilmeden hemen önce, kodumuzun çağrılmasını sağlayabiliriz. Bu kod, örneğin doğru görüntüleme için
form elemanlarına HTML sınıfları ekleyebilir. Kodu onRender
dizisine ekleriz:
$form->onRender[] = function ($form) {
BootstrapCSS::initialize($form);
};