Makra pro „ruční“ vykreslování formulářů
- Honza Marek
- Člen | 1664
Komu se nelíbí, že při vytváření formulářů vzniká v presenterech spousta kódu, který se zabývá spíše vzhledem než funkčností, mohl by ocenit sadu formulářových maker.
Co se s nima dá dokázat?
Presenter:
<?php
class TestPresenter extends BasePresenter {
protected function createComponentNazevFormu() {
$form = new Nette\Application\AppForm;
$form->addText("name");
$form->addSubmit("ok");
return $form;
}
}
?>
Šablona:
{form nazevFormu}
{widget nazevFormu errors} {* errory je potřeba ještě vymyslet *}
<p>{label name class => "big", text => "Jméno:"} {input name size => 30}</p>
<p>{input ok text => "Odeslat formulář"}</p>
{/form}
Akorát jsem ještě nevymyslel jak naložit s errorama, takže na tohle
téma bych rád otevřel diskuzi. Jsou dvě varianty – makra jako
{formErrors}
nebo {inputError text}
, u kterých by se
ale muselo někde bokem nastavit, jak mají vypadat html kontejnery chyb. Nebo
něco složitějšího jako
{ifError text}<span class="error">{errorText text}</span>{/if}
.
- romansklenar
- Člen | 655
Jdeš správným směrem, když jsem začal používat Rails, tak mi to zpětně v Nette taky vadilo. Teď už to vypadá skoro jako v těch Railsech :) Pro ty chyby by mohlo stačit něco jako prosté makro {errorMessages} nebo chceš definovat chyby hlášky chyb v šabloně? Tím, že se v Rails provádí validace v modelech, tak u formulářů, které jsou založené na modelech, se hlášky definují přímo v modelech. Jak to ale vyřešit na formulářích, které nejsou založené na modelech? Asi jedině validací při zpracování signálu formuláře a přidání chybové hlášky tam. Nevím právě jestli je rozumné tahat definice chybových hlášek do šablon.
Edit: Nevýhodou makra {errorMessages} by bylo, že může být použito pouze uvnitř bloku {form}.
- Honza Marek
- Člen | 1664
Hlášky do šablony tahat nechci, protože na jednom políčku ti může vzniknout x chyb a ty nevíš na kterou máš reagovat. Co je ale potřeba taky někde zadefinovat je to, jestli chyby mají být v ul li seznamu nebo v odstavcích se třídou error a tak. A to by možná v šabloně být mohlo. I když je to dost psaní navíc, tak nevim.
- Honza Marek
- Člen | 1664
Takže zatím takhle:
{form nazevFormu}
<p class="error" n:foreach="$formErrors as $error">{$error}</p>
<p>{label name class => "big", text => "Jméno:"} {input name size => 30}</p>
<p>{input ok text => "Odeslat formulář"}</p>
{/form}
Ještě když vymyslim nějak errory jednotlivých tlačítek, tak to bude paráda.
- Honza Marek
- Člen | 1664
honzakuchar napsal(a):
Nechceš to přidat do extras?
Já bych si spíš neskromně myslel, že něco podobného by mělo být rovnou ve frameworku :-D
- Jan Tvrdík
- Nette guru | 2595
Je to pěkné a navíc s kvalitou od Honzy Marka, jak to mám rád :) Těším se až to otestuji v praxi.
- Mikulas Dite
- Člen | 756
Honza Marek napsal(a):
Já bych si spíš neskromně myslel, že něco podobného by mělo být rovnou ve frameworku :-D
Rozhodně. Normální manuální render je hrozně dlouhý.
- Patrik Votoček
- Člen | 2221
Honza Marek napsal(a):
Já bych si spíš neskromně myslel, že něco podobného by mělo být rovnou ve frameworku :-D
Rejpnu si: Tak proč to není součástí nějákého forku na githubu? S požadavkem na pull…
- Honza Marek
- Člen | 1664
vrtak-cz napsal(a):
Rejpnu si: Tak proč to není součástí nějákého forku na githubu? S požadavkem na pull…
Protože potom se to nedá distribuovat samostatně. Ale můžu to taky zkusit.
- Honza Kuchař
- Člen | 1662
Já ty extrasy píšu už tak ze zvyku. :-) Ano, souhlasím se začlenením do frameworku. ;-)
- buff
- Člen | 63
Vypadá to skvěle. Rád bych to vyzkoušel, ale nedaří se mi to přeložit do PHP 5.2. Zkusil jsem odstranit namespace a ?: jsem doplnil o prostřední argument, ale nechodí to. Laděnka píše, že jí chybí ;, ale z výstupu je vidět, že toho chybí i trochu víc…
Line 46: <?php $formErrors = FormMacros::begin('admissionForm', $control, array())->getErrors() ?>
Line 47: <p><?php FormMacros::macroLabel% ?> <?php FormMacros::macroInput% ?></p>
Line 48: <p><?php FormMacros::macroInput% ?></p>
Line 49: <?php FormMacros::end() ?>
Neporadíte mi prosím někdo, jak na to?
- buff
- Člen | 63
Díky moc, Honzo, máš pravdu. Vzal jsem to při mazání těch namespaces trochu moc zhurta. ;-)
Stále to ale nechodí úplně podle mých představ. Nette křičí, že
postrádá druhý parametr u metody $form->addSubmit
a zřejmě
nepřímo i $form->addText
:
Warning: Missing argument 2 for FormContainer::addSubmit(), called in /.../app/presenters/HomepagePresenter.php on line 8 and defined in /.../Nette/Forms/FormContainer.php on line 365
Notice: Undefined variable: caption in /.../Nette/Forms/FormContainer.php on line 367
Když zadám jako druhý parametr prázdný string, u textu se label přepíše správně na „Jméno“, ale na tlačítku submit zůstane prázdný.
Mám Nette verze 0.9.4 stable.
A přihodím ještě ta makra.
<?php
//namespace Nette\Templates;
//use Nette\Forms\Form;
//use Nette\String;
/**
* Form macros
*
* @author Jan Marek
* @license MIT
*/
class FormMacros extends /*\Nette\*/Object {
private static $form;
public function __construct() {
throw new /*\*/InvalidStateException("Static class.");
}
public static function register() {
/* LatteMacros::$defaultMacros["form"] = '<?php %Nette\Templates\FormMacros::macroBegin% ?>';
LatteMacros::$defaultMacros["input"] = '<?php %Nette\Templates\FormMacros::macroInput% ?>';
LatteMacros::$defaultMacros["label"] = '<?php %Nette\Templates\FormMacros::macroLabel% ?>';
LatteMacros::$defaultMacros["/form"] = '<?php Nette\Templates\FormMacros::end() ?>'; */
LatteMacros::$defaultMacros["form"] = '<?php %FormMacros::macroBegin% ?>';
LatteMacros::$defaultMacros["input"] = '<?php %FormMacros::macroInput% ?>';
LatteMacros::$defaultMacros["label"] = '<?php %FormMacros::macroLabel% ?>';
LatteMacros::$defaultMacros["/form"] = '<?php FormMacros::end() ?>';
}
public static function macroBegin($content) {
list($name, $modifiers) = self::fetchNameAndModifiers($content);
//return "\$formErrors = Nette\Templates\FormMacros::begin($name, \$control, $modifiers)->getErrors()";
return "\$formErrors = FormMacros::begin($name, \$control, $modifiers)->getErrors()";
}
public static function begin($form, $control, $modifiers = array()) {
if ($form instanceof Form) {
self::$form = $form;
} else {
self::$form = $control[$form];
}
if (isset($modifiers["class"])) {
self::$form->getElementPrototype()->class[] = $modifiers["class"];
}
self::$form->render("begin");
return self::$form;
}
public static function end() {
self::$form->render("end");
}
public static function macroInput($content) {
list($name, $modifiers) = self::fetchNameAndModifiers($content);
//return "Nette\Templates\FormMacros::input($name, $modifiers)";
return "FormMacros::input($name, $modifiers)";
}
public static function input($name, $modifiers = array()) {
$input = self::$form[$name]->getControl();
if (isset($modifiers["size"])) {
$input->size($modifiers["size"]);
}
if (isset($modifiers["rows"])) {
$input->rows($modifiers["rows"]);
}
if (isset($modifiers["cols"])) {
$input->cols($modifiers["cols"]);
}
if (isset($modifiers["class"])) {
$input->class[] = $modifiers["class"];
}
if (isset($modifiers["style"])) {
$input->style($modifiers["style"]);
}
echo $input;
}
public static function macroLabel($content) {
list($name, $modifiers) = self::fetchNameAndModifiers($content);
//return "Nette\Templates\FormMacros::label($name, $modifiers)";
return "FormMacros::label($name, $modifiers)";
}
public static function label($name, $modifiers = array()) {
$label = self::$form[$name]->getLabel();
if (isset($modifiers["text"])) {
$label->setText($modifiers["text"]);
}
if (isset($modifiers["class"])) {
$label->class[] = $modifiers["class"];
}
if (isset($modifiers["style"])) {
$label->style($modifiers["style"]);
}
echo $label;
}
// helper
private static function fetchNameAndModifiers($code) {
$name = LatteFilter::fetchToken($code);
$modifiers = LatteFilter::formatArray($code);
$name = String::startsWith($name, '$') ? $name : "'$name'";
$modifiers = $modifiers ? $modifiers : "array()";
return array($name, $modifiers);
}
}
?>
Editoval buff (5. 5. 2010 13:58)
- Honza Marek
- Člen | 1664
Warning: Missing argument 2 for FormContainer::addSubmit()
To křičení souvisí s tím, že ty parametry jsou v nette nepovinné až od tohoto commitu. Ve stable verzi nette tam bude potřeba zadávat null nebo něco.
Když zadám jako druhý parametr prázdný string, u textu se label přepíše správně na „Jméno“, ale na tlačítku submit zůstane prázdný.
To jsem koukám u inputů zapomněl podporovat. V metodě FormMacros::input se musí přidat jeden if, který to bude řešit.
- buff
- Člen | 63
To jsem koukám u inputů zapomněl podporovat. V metodě FormMacros::input se musí přidat jeden if, který to bude řešit.
Přidal jsem si tam
<?php
if (isset($modifiers["value"])) {
$input->value($modifiers["value"]);
}
?>
a místo {input ok text => "Odeslat formulář"}
píšu
{input ok value => "Odeslat formulář"}
. Funguje to bezvadně,
taky bych to rád viděl přímo ve frameworku ;-) Díky!
- Nich
- Člen | 49
Chtěl bych se zeptat, jestli je fakt jedno kam vrazím v „bootstrap.php“ tenhle script…
Nette\Templates\FormMacros::register();
protože mi debugger vyhazuje
Fatal Error
Class 'Nette\Templates\FormMacros' not found
Line 60: Nette\Templates\FormMacros::register();
A přitom sem soubor „FormMacros.php“ nacpal do složky „libs/Nette/Templates/“ takže fakt netuším proč mi to nechce najít tu classu…
- Nich
- Člen | 49
Chtěl bych se zeptat jak do takovýho formuláře naházet pravidla, protože mi to vykopne
Fatal Error
Class 'Form' not found
Line 9: ->addRule(Form::FILLED, 'Zadejte prosím své jméno');
a to i když jsem v robotovy načetl „libs/Nette/Forms“ a v bootstrapu jsem dal „use Nette\Forms;“
- potapnik
- Člen | 127
Souhlasím s tím, že toto by mělo být součástí frameworku. Standardní generování přes AppForm je paráda, ale pár formulářů dokáže Presenter pěkně znepřehlednit a zahustit, navíc tohle se dá i parádně lokalizovat přímo v šabloně a nemusím cpát lokalizaci do Presenteru. Velké díky! Nettem se pořád přehrabuju, ale tahle feature mě napadla hned, když jsem zkoušel pochopit první krůčky :-)
- stanley
- Člen | 22
Zdravim vsechny diskutujici!
Pravdepodobne banalni dotaz od zacatecnika: Pouzivam makra od Honzy Marka a
snazim se dostat hodnotu z promenne do textarea v sablone. Zapis
{input articleContent value => $articleContent}
mi obsah
promenne nacpe do atributu value textarea, nikoliv do obsahu tohoto paroveho
tagu, nehlede na fakt, ze obsahem promenne je HTML (z textarea se posleze
vyrobi tinyMCE editor), takze bych spis potreboval neco jako
{input articleContent value => !$articleContent}
, coz ale pro
jistotu neudela vubec nic…
Co delam spatne? Dik. :)
- Cifro
- Člen | 245
stanley napsal(a):
Zapis
{input articleContent value => $articleContent}
Co delam spatne? Dik. :)
Děláš špatne to, že input naplňaš v šablone. Mal by si naplňať už v presentery. Napr.:
// niekde v render<Nieco> metode...
$this['tvojFormular']['articleContent']->setValue('nejaky obsah');
Macro {input}
nepozná atribút value
. Tieto makrá
sú len na vypisovanie formulára už aj s hodnotami. Hodnoty plniš
v presentery.
Editoval Cifro (6. 7. 2010 20:17)
- stanley
- Člen | 22
O naplneni inputu mimo sablonu jsem se pokousel, ale marne… Problem je
v tom, ze formular neni v prezenteru, ale v komponente. Tahle komponenta ma
standardne vypsat obsah clanku a pri obdrzeni signalu edit!
ma
namisto obsahu clanku vypsat formular pro editaci obsahu clanku. Komponenta se
v sablone prezenteru vola {widget articleContentEdit $lang, $path}
a v komponente mam metodu render( $lang, $path )
, ktera podle
svych argumentu nacte text clanku z databaze a vlozi ho do sve sablony. Kdyz
ale do komponenty pridam metodu handleEdit( $lang, $path )
, tak ta
nedostane ty parametry $lang
, $path
a nemam tedy jak
poznat, ktery clanek nacist. Pritom by mi stacilo v handleEdit
nastavit jinou sablonu komponenty a vyrobit formular, text clanku se do sablony
vlozi kazdopadne v render
, takze uz je to jenom problem toho jak
v sablone nakreslit prislusny formularovy prvek a jeho obsah tak, aby to
davalo smysl…
- Davelister
- Člen | 28
Toto ve frameworku opravdu chybi, standartní cesta je neskutečne zdlouhava a o prehlednosti radeji ani nemluvim :)
Btw, docela by se hodilo makro pro zjisteni zda input existuje, neco jako
{if inputExists(name)}
…
{/if}
- Davelister
- Člen | 28
Trochu jsem tridu upravil, aby byla pouzitelna i pro prvky ktere jsou
v FormContaineru. Treba se to nekomu bude hodit
Je to upravena verze bez namespaces od buffa.
Pouziti:
Formular:
function createComponentForm(){
$form = new Form;
$form->addContainer('container')
->addText('inputText','xx');
}
Sablona:
{input container.inputText}
Upravena trida:
<?php
//namespace Nette\Templates;
//use Nette\Forms\Form;
//use Nette\String;
/**
* Form macros
*
* @author Jan Marek
* @license MIT
*/
class FormMacros extends /*\Nette\*/Object {
private static $form;
public function __construct() {
throw new /*\*/InvalidStateException("Static class.");
}
public static function register() {
/* LatteMacros::$defaultMacros["form"] = '<?php %Nette\Templates\FormMacros::macroBegin% ?>';
LatteMacros::$defaultMacros["input"] = '<?php %Nette\Templates\FormMacros::macroInput% ?>';
LatteMacros::$defaultMacros["label"] = '<?php %Nette\Templates\FormMacros::macroLabel% ?>';
LatteMacros::$defaultMacros["/form"] = '<?php Nette\Templates\FormMacros::end() ?>'; */
LatteMacros::$defaultMacros["form"] = '<?php %FormMacros::macroBegin% ?>';
LatteMacros::$defaultMacros["input"] = '<?php %FormMacros::macroInput% ?>';
LatteMacros::$defaultMacros["label"] = '<?php %FormMacros::macroLabel% ?>';
LatteMacros::$defaultMacros["/form"] = '<?php FormMacros::end() ?>';
}
public static function macroBegin($content) {
list($name, $modifiers) = self::fetchNameAndModifiers($content);
//return "\$formErrors = Nette\Templates\FormMacros::begin($name, \$control, $modifiers)->getErrors()";
return "\$formErrors = FormMacros::begin($name, \$control, $modifiers)->getErrors()";
}
public static function begin($form, $control, $modifiers = array()) {
if ($form instanceof Form) {
self::$form = $form;
} else {
self::$form = $control[$form];
}
if (isset($modifiers["class"])) {
self::$form->getElementPrototype()->class[] = $modifiers["class"];
}
self::$form->render("begin");
return self::$form;
}
public static function end() {
self::$form->render("end");
}
public static function macroInput($content) {
list($name, $modifiers) = self::fetchNameAndModifiers($content);
//return "Nette\Templates\FormMacros::input($name, $modifiers)";
return "FormMacros::input($name, $modifiers)";
}
public static function input($name, $modifiers = array()) {
$input = self::getComponentFromName($name)->getControl();
if (isset($modifiers["size"])) {
$input->size($modifiers["size"]);
}
if (isset($modifiers["rows"])) {
$input->rows($modifiers["rows"]);
}
if (isset($modifiers["cols"])) {
$input->cols($modifiers["cols"]);
}
if (isset($modifiers["class"])) {
$input->class[] = $modifiers["class"];
}
if (isset($modifiers["style"])) {
$input->style($modifiers["style"]);
}
echo $input;
}
public static function macroLabel($content) {
list($name, $modifiers) = self::fetchNameAndModifiers($content);
//return "Nette\Templates\FormMacros::label($name, $modifiers)";
return "FormMacros::label($name, $modifiers)";
}
public static function label($name, $modifiers = array()) {
$label = self::getComponentFromName($name)->getLabel();
if (isset($modifiers["text"])) {
$label->setText($modifiers["text"]);
}
if (isset($modifiers["class"])) {
$label->class[] = $modifiers["class"];
}
if (isset($modifiers["style"])) {
$label->style($modifiers["style"]);
}
echo $label;
}
/**
* Returns component for name
*
* @param string $name component.subcomponent
*/
private static function getComponentFromName($name){
$name = split('\.',$name);
$current = self::$form;
foreach($name as $n){
$current = $current->getComponent($n);
}
return $current;
}
// helper
private static function fetchNameAndModifiers($code) {
$name = LatteFilter::fetchToken($code);
$modifiers = LatteFilter::formatArray($code);
$name = String::startsWith($name, '$') ? $name : "'$name'";
$modifiers = $modifiers ? $modifiers : "array()";
return array($name, $modifiers);
}
}
?>
Editoval Davelister (13. 7. 2010 23:05)
- Mikulas Dite
- Člen | 756
knyttr napsal(a):
Používáte někdo tato makra s vývojovou verzí Nette? Při jejich použití mi to vyhazuje výjimku na řádku 963 v Latte Macros:
Using $this in non-context:
$tokens = $this->parseMacro($input);
Zkoušel jsem, tohle se mi zrovna nastalo.
Jenom mi to nefunguje pokud prvek je required
. Z nějakého
důvodu to pak vyhazuje chybu Undefined index: :filled
v
Line 82: public static function input($name, $modifiers = array())
Line 83: {
Line 84: $input = self::$form[$name]->getControl();
Editoval Mikulas Dite (3. 11. 2010 7:01)
- Mikulas Dite
- Člen | 756
Mikulas Dite napsal(a):
Jenom mi to nefunguje pokud prvek je
required
. Z nějakého důvodu to pak vyhazuje chybuUndefined index: :filled
.
Kupodivu je to celé problém mezi
s formmacros nefunguje:
$form->addText('text')
->setRequired();
funguje:
$form->addText('text')
->setRequired(TRUE);
- knyttl
- Člen | 196
Když se do toho dívám dále, očividně je problém v tom, že ve FormMacros je formatArray voláno staticky, přitom v LatteMacros statické není a volá $this->.
<?php
private static function fetchNameAndModifiers($code) {
$name = LatteFilter::fetchToken($code);
$modifiers = LatteFilter::formatArray($code);
...
?>
a pak LatteMacros:
<?php
964: public function formatArray($input, $prefix = '')
965: {
966: $tokens = $this->parseMacro($input);
?>
Mohu potvrdit, že v předchozí verzi Nette, kde mi vše funguje korektně, funkce statická nebyla a díky tomu vše fungovalo.
Může autor naznačit řešení?
- Vyki
- Člen | 388
knyttr napsal(a):
Když se do toho dívám dále, očividně je problém v tom, že ve FormMacros je formatArray voláno staticky, přitom v LatteMacros statické není a volá $this->.
Není to statické od 26.10. https://github.com/…8d940ccbcc88. Předtím to statické bylo.
- Mikulas Dite
- Člen | 756
Já používám svůj fork https://gist.github.com/608501, kde jsem si prostě latte oživil a nevolám tu funkci staticky.
- RDPanek
- Člen | 189
Ahoj, snažím se ručně vykreslit formulář – vše zatím v pohodě, ale u selectu mi to blbne…
V továrničce
$form->addSelect('type', array('sdsd', 'sdasd'));
a v šabloně
<td>{input type class => 'selectForm radiusForm'}</td>
a výsledek:
InvalidArgumentException
Textual content must be a scalar, array given.
Nette\Web\Html.php Line: 229
Line 229: throw new InvalidArgumentException(„Textual content must be a
scalar, " . gettype($html) .“ given.");
v čem je problém? díky.
- Jan Tvrdík
- Nette guru | 2595
Mírně vylepšenou verzi FormMacros
kompatibilní
s aktuálním Nette najdete tady:
http://nette.merxes.cz/form-macros/
- cirdaz
- Člen | 21
zkousim testovat http://nette.merxes.cz/form-macros
a vyhodí mi to chybu FormMacros se nacte, ale z nejakeho duvodu to nefakci pls
o radu
<?php
Component with name 'name' does not exist.
304: * Returns form control.
305: *
306: * @param string input name
307: * @return Nette\Forms\IFormControl
308: */
309: protected static function getControl($name)
310: {
311: return end(self::$containerStack)->getComponent($name);
312: }
313:
?>