ConventionalRenderer → 20 řádků kódu
- David Grudl
- Nette Core | 8218
Jestli se mi nějaká část frameworku skutečně nelíbí, tak to je ConventionalRenderer. A nejde jen o samotný kód této třídy, ale hlavně o to, že doménu šablon přenáši do presenteru, kde se musí čarovat s objekty Html a různými options, aby se docílilo požadovaného vykreslení (a mnohdy to ani nejde). V presenteru by přitom měla být jen prostá definice formuláře a validačních pravidel, nic co souvisí s vykreslováním.
Samozřejmě je možné formulář kreslit manuálně (a tím se vlastně všechno vyřeší), ale lenost programátora se tomu vzpouzí.
Dnes už je možné téměř celý slavný ConventionalRenderer nahradit cca 20 řádky šablony s CurlyBracketsFilterem:
{control $form begin}
<!-- hidden fields -->
<div n:foreach="$form->getComponents(TRUE, 'Nette\Forms\HiddenField') as $control">{$control->control}</div>
<!-- errors -->
{assign errors => $form->errors}
<ul class="error" n:if="$errors" n:block="#errors">
<li n:foreach="$errors as $error">{$error}</li>
</ul>
<!-- groups -->
<fieldset n:foreach="$form->groups as $group" n:if="$group->controls && $group->getOption('visual')" n:block="#group">
<legend n:ifset="$group->options['label']">{$group->options['label']}</legend>
<p n:ifset="$group->options['description']">{$group->options['description']}</p>
<table n:block="#controls">
<tr {attr class('required', $control->getOption('required'))} n:foreach="$group->controls as $control" n:if="!$control->getOption('rendered') && $control->getForm(FALSE) === $form" n:block="#pair">
<th n:block="#label">{if $control instanceof Button || $control instanceof Checkbox} {else}{!$control->label}{/if}</th>
<td n:block="#control">{!$control->control}{if $control instanceof Checkbox}{!$control->label}{/if}</td>
</tr>
</table>
</fieldset>
<!-- non-group -->
{include #controls, group => $form}
{control $form end}
Uvedený kód funguje, abyste jej mohli vyzkoušet, stačí jej uložit do
souboru @form.phtml
a místo
{control myForm}
použít
{include '@form.phtml', form => $presenter['myForm']}
Pro celý web pak může stačit jedna definice šablony. A co když bych
někde potřeboval mírně upravenou šablonu, třeba s vykreslováním
chybových zpráv přímo pod každým prvkem? Lze použít dědění. Místo
souboru @form.phtml
vložím @form-special.phtml
s tímto obsahem:
{extends '@form.phtml'}
<td n:block="#control">
{!$control->control}{if $control instanceof Checkbox}{!$control->label}{/if}
{if $control->errors}{include #errors, errors => $control->errors}{/if}
</td>
Tohle celé berte prosím jako nástin, jako koncepci která má k hotovému řešení ještě hodně daleko.
- Honza Marek
- Člen | 1664
Jo.. kdysi jsem se taky o něco podobného pokoušel. Chtěl jsem to udělat jako vlastní renderer, který využívá šablony.
A jestli se mi teda nějaká část Nette nelíbí, tak je to standardní ClientScript, protože je naprosto nepřizpůsobitelný.
- David Grudl
- Nette Core | 8218
Honza M. napsal(a):
A jestli se mi teda nějaká část Nette nelíbí, tak je to standardní ClientScript, protože je naprosto nepřizpůsobitelný.
Ten je hned na řadě ;)
- Honza Kuchař
- Člen | 1662
David Grudl napsal(a):
Honza M. napsal(a):
A jestli se mi teda nějaká část Nette nelíbí, tak je to standardní ClientScript, protože je naprosto nepřizpůsobitelný.
Ten je hned na řadě ;)
Hurá! :)
- David Grudl
- Nette Core | 8218
Všechny tři údaje je možné při definici formuláře vynechat a nastavit až v šabloně:
<td>{$form['textarea']->label->setText('Poznamka')}</td>
<td>{$form['textarea']->control->col(10)->row(20)}</td>
ps. možná by se dalo rozšířit getLabel(), tak aby fungovalo i překládání:
<td>{$form['textarea']->getLabel('Poznamka')}</td>
- Yrwein
- Člen | 45
Jinak ještě co se týče clientscriptu, jen takové menší šťouchnutí
ku přemýšlení nad výslednou podobou – bylo by skvělé, kdyby ho šlo
nějak snadněji integrovat se skripty ala jQuery form validator .
// Jen příklad, který mě kdysi zaujmul. .)
Editoval Yrwein (21. 8. 2009 10:49)
- Honza Kuchař
- Člen | 1662
Yrwein napsal(a):
bylo by skvělé, kdyby ho šlo nějak snadněji integrovat se skripty ala jQuery form validator .
// Jen příklad, který mě kdysi zaujmul. .)
Wow, to by bylo super! Ale vidím to spíš na vlastní ClientScript.
Používám něco podobného v to Editaovatelném datagridu:
- phx
- Člen | 651
Zdravim…
To je nejaky novy zapis?
<ul class="error" n:if="$errors" n:block="#errors">
<li n:foreach="$errors as $error">{$error}</li>
</ul>
Hledam na foru a tohle vidim jako prvni zminku. Prosim o nasmerovani na spravne misto.
Jinak pokud je to novinka tak se mi zamlouva, protoze mam vecne dilema s tim jak odsazovat. Protoze kdyz budeu odsazovat logicky programatorsky
<div>
{if $foo}
<p>BAR</p>
{/if}
</div>
tak vysledny HTML kod je zvlasne odsazen. A jeste vtipnejsi to je u posilani plaintext mailu nebo <pre> kde zalezi na kazdem bilem znaku.
- phx
- Člen | 651
LOL. Promaza? Nic tam krome Davida nezbylo.
DIKY.
EDIT:
Ha tam jsem to nasel v RSS:) No celkem cahpu proc to promazal, ale skoro pulka
tam byla vecna a uzitecna. I kdyz asi bych to hodil do samostatneho
filteru.
I kdyz me osobne to je fuk zda ala XML nebo {}. Jedine co me trapi se problem s bilymi znaky.
Editoval phx (26. 8. 2009 11:05)
- Patrik Votoček
- Člen | 2221
parametry samozdřejmě makro widget ma… v tomto pripade se bude volat:
$presenter['myForm']->render('@form.phtml');
takže pokud nastavíš formu svůj renderer kterej bude předpokládat že parametr je šablona tak to bude fungovat…
Trochu OT: widget MyForm nefunguje!!! první písmeno musí být malé!!! (Davide už chápeš proč jsme to na PS tak řešily???)
- David Grudl
- Nette Core | 8218
Musím si příště dělat zápisy z porady. Takže jako fakt? První casein, ostatní case? Jaké byly argumenty proti celému casein?
- Honza Marek
- Člen | 1664
David Grudl napsal(a):
Musím si příště dělat zápisy z porady. Takže jako fakt? První casein, ostatní case? Jaké byly argumenty proti celému casein?
Myslim, že se ti to prostě nelíbilo, takže jsi argumentoval jakousi zpětnou nekompatibilitou, kdyby někdo používal komponenty s názvy odlišenými jen podle velikostí písmen :)
- skrivy
- Člen | 51
Ahoj,
v úvodním příspěvku je napsáno, že se jedná o nástin. Rád bych věděl, jestli se to už dostalo do nějaké finální zdokumentované verze či nikoliv? Primárně mě zajímá oddělení definice formuláře a validačních pravidel v presenteru a nastavení zobrazení ve view, jak je tady někde ve fóru psáno. Docela se mi ta myšlenka líbí :).
Díky
Editoval skrivy (30. 11. 2009 13:20)
- meris
- Člen | 8
Velmi pěkné řešení, mám v něm ale jeden problém a to jak nastavit
pomocí tohoto řešení parametry formuláře?
Například mám formulář pro editaci zboží v databázi a chci tento
formulář předvyplnit údaji z databáze?
toto řešení
widget $form param, param2
je sice funkční ale bohužel potřebuji formulář přestylovat, takže bych rád použil toto čistší řešení.
Tato řešení mi nefungují
widget $form begin, param, param2
widget $form param, param2, begin
Editoval meris (8. 12. 2009 21:29)
- meris
- Člen | 8
meris napsal(a):
Jak nastavit pomocí tohoto řešení parametry formuláře?
Tak jsem se nad tím ještě jednou pořádně zamyslel, jisté řešení mě napadlo, ale má své mouchy, především data spojuje na úrovní šablony nikoli na úrovní presenteru či modelu a to mi nepřijde moc šikovné.
Volání šablony pro formulář
{include '@form.phtml', form => $presenter['myform'], default => $formParam}
Úprava šablony, kde se vloží tento kód:
{control $form begin }
{if (isset($default))}
{foreach $default as $key => $value}
{? $form[$key]->setDefaultValue($value);}
{/foreach}
{/if}
- skrivy
- Člen | 51
Ahoj,
tak jsem to začal používat – ač trochu poupraveně a musím říct, že je to super.
Jenom mě mám trochu nesrovnalosti s chybovýma hláškama … do FormControl.php bych přidal do methody addError toto:
if (self::$updateFormErrors)
$this->getForm()->addError($message);
Principielne to pouzivam tak, ze ke kazdemu policku vypisuju lokalni chybu, ktera se k nemu vztahuje (tzn. spatne vyplneny email, nezadany obsah, atd.) a nad formular vypisuju chyby pri zpracovani (server je nedostupny nebo nastal nejakej jinej problem, kterej se nevztahuje k zadnemu policku).
Co Vy na to? Je to spravne nebo se to resi jinak?
- Filip Procházka
- Moderator | 4668
Ahoj, chtěl bych poprosit Davida, jestli by někam nehodil novou verzi, se kterou se nám chlubil při preview Nette 2beta a dokonce bych i řekl, že sliboval, že to pak někam hodí :) A i kdyby nesliboval, pár lidí by to určitě ocenilo. Díky :)
- nanuqcz
- Člen | 822
Právě jsem strávil nějakých pár minut předěláváním na novou verzi Nette, tak to hodím i sem, ať z toho mají užitek i ostatní:
{control $form begin}
<!-- hidden fields -->
<div n:foreach="$form->getComponents(TRUE, 'Nette\Forms\Controls\HiddenField') as $control">{$control->control}</div>
<!-- errors -->
{assign errors => $form->errors}
<ul class="error" n:if="$errors" n:block="#errors">
<li n:foreach="$errors as $error">{$error}</li>
</ul>
<!-- groups -->
<fieldset n:foreach="$form->groups as $group" n:if="$group->controls && $group->getOption('visual')" n:block="#group">
<legend n:ifset="$group->options['label']">{$group->options['label']}</legend>
<p n:ifset="$group->options['description']">{$group->options['description']}</p>
<table n:block="#controls">
<tr n:class="$control->getOption('required')? required" n:foreach="$group->controls as $control" n:if="!$control->getOption('rendered')" n:block="#pair">
<th n:block="#label">{if $control instanceof Button || $control instanceof Checkbox} {else}{!$control->label}{/if}</th>
<td n:block="#control">{!$control->control}{if $control instanceof Checkbox}{!$control->label}{/if}</td>
</tr>
</table>
</fieldset>
<!-- non-group -->
{include #controls, group => $form}
{control $form end}
Bohužel
n:if="!$control->getOption('rendered') && $control->getForm(FALSE) === $form"
jsem musel z bloku pair
vyhodit, protože to házelo chybu, kterou
se mi nepodařilo opravit (Undefined variable $form
).
I tak se ale zdá, že to funguje OK (zatím jsem to moc netestoval).
Editoval xxxObiWan (27. 8. 2011 16:42)
- Jan Tvrdík
- Nette guru | 2595
HiddenField
má špatný namespace. Oproti tomu, co prezentoval
David tam chybí pár podstatných (!) věcí, které umožňovali předefinovat
určitý pár pomocí bloků.
- nanuqcz
- Člen | 822
Já nevím, co prezentoval David :-) Snažil jsem se jen najít nějaké
funkční řešení, za jakékoli lepší budu vděčný ;-)
HiddenField
zítra opravím v mojem předešlém přízpěvku
(až se vyspím :-))
EDIT: `HiddenField v předešlém přízpěvku opraven
Editoval xxxObiWan (27. 8. 2011 16:43)
- Jan Tvrdík
- Nette guru | 2595
HosipLan wrote:
Ahoj, chtěl bych poprosit Davida, jestli by někam nehodil novou verzi, se kterou se nám chlubil při preview Nette 2beta a dokonce bych i řekl, že sliboval, že to pak někam hodí :) A i kdyby nesliboval, pár lidí by to určitě ocenilo. Díky :)
+1
- David Grudl
- Nette Core | 8218
Ta „nová“ verze zas tak nová není, protože vznikla před existencí
formulářových maker, které by bylo fajn využít. form.latte
:
(Příklad jsem zaktualizoval pro Nette 2.1 a vyšší)
<form n:name=$form>
<ul class="error" n:if="$form->ownErrors" n:block="#errors">
<li n:foreach="$form->ownErrors as $error">{$error}</li>
</ul>
<fieldset n:foreach="$form->groups as $group" n:if="$group->controls" n:block="#group">
<legend n:ifset="$group->options[label]">{$group->options[label]}</legend>
<p n:ifset="$group->options[description]">{$group->options[description]}</p>
<table n:block="#controls">
{foreach $group->controls as $field}
<tr n:if="!$field->getOption(rendered)" n:class="$field->required ? required" n:block="$field->name.'-row'">
<th n:block="#label">{label $field /}</th>
<td n:block="#control">{input $field} {inputError $field}</td>
</tr>
{/foreach}
</table>
</fieldset>
{include #controls, group => $form}
</form>
a příklad použití:
{includeblock 'form.latte', form => signInForm}
{define #username-row}
<tr>
<th colspan=2>radek prvku 'username' vykreslime jinak:<br> {$field->label} {$field->control}</th>
</tr>
{/define}
- nanuqcz
- Člen | 822
Mírně jsem updatnul skript od Davida (nově vykresluje u inputů description).
{form $form}
<div n:foreach="$form->getComponents(TRUE, 'Nette\Forms\Controls\HiddenField') as $field">{$field->control}</div>
<ul class="error" n:if="$form->errors" n:block="#errors">
<li n:foreach="$form->errors as $error">{$error}</li>
</ul>
<fieldset n:foreach="$form->groups as $group" n:if="$group->controls" n:block="#group">
<legend n:ifset="$group->options[label]">{$group->options[label]}</legend>
<p n:ifset="$group->options[description]">{$group->options[description]}</p>
<table n:block="#controls">
{foreach $group->controls as $field}
<tr n:if="!$field->getOption('rendered')" n:class="$field->required ? required" n:block="$field->name.'-row'">
<th n:block="#label">{if $field instanceof Nette\Forms\Controls\Button || $field instanceof Nette\Forms\Controls\Checkbox} {else}{!$field->label}{/if}</th>
<td n:block="#control">
{!$field->control}{if $field instanceof Nette\Forms\Controls\Checkbox}{!$field->label}{/if}
<small n:if="isset($field->options['description'])">{$field->options['description']}</small>
</td>
</tr>
{/foreach}
</table>
</fieldset>
{include #controls, group => $form}
{/form}
P.S. Pokud ve formulářích používáte addGroup()
a budou se
vám některé řádky formuláře vykreslovat dvakrát, pak nezapomňte do
vlastního bloku řádku přidat podmínku (právě jsem nad tím strávil
hodinu casu :-)):
{define #username-row}
{if !$field->getOption('rendered')}
radek prvku 'username' vykreslime jinak: {$field->label} {$field->control}
{/if}
{/define}
Editoval nanuqcz (3. 10. 2012 10:50)
- llook
- Člen | 407
duskohu napsal(a):
Caute asi blba otazka, ale mohli by ste mi niekto povedat co robi tato cast:
n:block="#group"
v tomto?
<fieldset n:foreach="$form->groups as $group" n:if="$group->controls" n:block="#group">
Definuje blok, takže si ho v případě potřeby můžeš předefinovat, pokud nechceš použít normální fieldset, ale nějaký jiný markup:
<div n:foreach="$form->groups as $group" n:if="$group->controls" n:define="#group">
<marquee n:ifset="$group->options[label]">{$group->options[label]}</marquee>
{include #controls, group => $group}
</div>
{includeblock 'form.latte', form => $presenter[signInForm]}