Přiohnutí makra {label /} pro vlastní účely
- uestla
- Backer | 799
Dobrý podvečer.
Co by Latte API neznalý a časem do kouta zatlačený jedinec, časových prostředků pro nastudování dokumentace nemaje, vás tímto žádám o pomoc:
Potřebuji ohnout makro {label /}
tak, aby za text uvnitř
připojilo mnou specifikovaný text/HTML element v závislosti na tom, je-li
prvek, ke kterému se label pojí, ve formuláři uveden jako povinný.
Příklad:
{label username}Uživatelské jméno{/label}
<!-- vygeneruje -->
<label for="...">Uživatelské jméno <span class="req">*</span></label>
<!-- s tím, že <span /> se připojí, neboť $form['username']->required === TRUE -->
Problém mám s tím, že nevím, kde přesně si definici tohoto form-makra přepsat (zda v Presenteru či bootstrapu, či …) a hlavně jakým stylem.
Předem moc děkuji za jakoukoli pomoc.
Editoval uestla (2. 11. 2011 17:04)
- Vojtěch Dobeš
- Gold Partner | 1316
V presenteru:
public function templatePrepareFilters($template)
{
$latte = new Nette\Latte\Engine;
$template->registerFilter($latte);
$myMacros = new Nette\Latte\Macros\MacroSet($latte->parser);
$myMacros->addMacro('label', function ($node, $writer) {
$cmd = 'if ($_label = $form[%node.word]->getLabel()) echo $_label->addAttributes(%node.array)';
if ($node->isEmpty = (substr($node->args, -1) === '/')) {
$node->setArgs(substr($node->args, 0, -1));
return $writer->write($cmd);
} else {
return $writer->write($cmd . '->startTag()');
}
}, function ($node, $writer) {
$cmd = 'if ($form[%node.word]->isRequired()) ?><span class="req">*</span><?php endif';
return $writer->write($cmd . '?></label><?php');
});
}
Případně, pokud budeš chtít tento kód sdílet i v komponentách, tak
si třeba vytvořit službu latte
:
# bootstrap.php
$container->latte = function ($context) {
$latte = new Nette\Latte\Engine;
$myMacros = new Nette\Latte\Macros\MacroSet;
// ... to samé co výše
};
A v presenteru (a stejně tak v komponentách, tedy potomcích
Control
) pouze:
public function templatePrepareFilters($template)
{
$template->registerFilter($this->/*presenter->*/context->latte);
}
- uestla
- Backer | 799
Děkuji.
Jak se ale zdá, tak v callbacku pro koncovou značku (tj. třetí parametr
addMacro()
metody) se nějak ztratí expandovaná proměnná
%node.word
a ve zkompilované šabloně pak zůstane jenom
$form[""]->isRequired()
.
Je to bug nebo feature? Díky.
// EDIT:
Při fetchi se totiž %node.word vycucne, čili při dalším přístupu už tam není… Jak z toho ven?
// EDIT 2:
Metodou dřevorubec jsem dospěl k následujícímu „řešení“:
class MyFormMacros extends \Nette\Latte\Macros\MacroSet
{
/** @var string */
public $__word;
public static function install(\Nette\Latte\Parser $parser)
{
$me = new static($parser);
$me->addMacro('label', function (\Nette\Latte\MacroNode $node, \Nette\Latte\PhpWriter $writer) use ($me) {
$me->__word = $writer->formatWord( $node->tokenizer->fetchWord() );
$cmd = 'if ($_label = $form[' . $me->__word . ']->getLabel()) echo $_label->addAttributes(%node.array)';
if ($node->isEmpty = (substr($node->args, -1) === '/')) {
$node->setArgs(substr($node->args, 0, -1));
return $writer->write($cmd);
} else {
return $writer->write($cmd . '->startTag()');
}
}, function ($node, $writer) use ($me) {
$cmd = 'if ($form[' . $me->__word . ']->required): ?> <span class="req">*</span><?php endif';
return $writer->write($cmd . '?></label><?php');
});
}
}
Pro nepozorné si fetchované %node.word
ukládám do property
třídy tak, abych k ní mohl přistoupit i v pozdní fázi (tj. ve druhém
callbacku).
// EDIT 3:
Drobné úpravy ve fetchnutí slova…
Editoval uestla (2. 11. 2011 23:46)
- Vojtěch Dobeš
- Gold Partner | 1316
Jop, mám dojem, že to už někdo ají reportoval. Úplně nej
best-practice
je pak asi podědit MacroSet
, jak to
vlastně dělá i samotné Nette, a tu pomocnou členskou proměnnou
zprivatit.
- uestla
- Backer | 799
Ještě si dovolím malinko ztížit svou situaci ;-)
Daný <span />
bych rád vykresloval i v případě, že
zavolám label prázdný, čili {label username /}
. Kontrola
prázdnosti se vyskytuje při přidávání makra v prvním callbacku, ale
netuším, jak docílit kýženého výsledku (čili mít připojený span jak
pokud vlastní label uvedu, tak pokud chci použít výchozí).
Předem děkuji
- Vojtěch Dobeš
- Gold Partner | 1316
Tak to bych udělal tak :)…
class MyFormMacros extends \Nette\Latte\Macros\MacroSet
{
/** @var string */
public $__word;
public static function install(\Nette\Latte\Parser $parser)
{
$me = new static($parser);
$me->addMacro('label', function (\Nette\Latte\MacroNode $node, \Nette\Latte\PhpWriter $writer) use ($me) {
$me->__word = $writer->formatWord( $node->tokenizer->fetchWord() );
$cmd = 'if ($_label = $form[' . $me->__word . ']->getLabel()) echo $_label->addAttributes(%node.array)';
if ($node->isEmpty = (substr($node->args, -1) === '/')) {
$node->setArgs(substr($node->args, 0, -1));
return $writer->write($cmd . '; if ($form[' . $me->__word . ']->required) echo \'<span class="req"><?php\'');
} else {
return $writer->write($cmd . '->startTag()');
}
}, function ($node, $writer) use ($me) {
$cmd = 'if ($form[' . $me->__word . ']->required): ?> <span class="req">*</span><?php endif';
return $writer->write($cmd . '?></label><?php');
});
}
}
Nejsem si teď jistý, jestli přesně tohle bude fungovat, ale myšlenka je snad jasná – prostě to tam zkopčit.
- uestla
- Backer | 799
Děkuji!
Problém je, že je-li makro prázdné, rovnou se vykresluje NHtml element, bylo třeba se tedy porochnit ještě v této třídě a vykoumat následující (zkráceno pouze na kýženou větev):
if ($node->isEmpty = (substr($node->args, -1) === '/')) {
$node->setArgs(substr($node->args, 0, -1));
return $writer->write($cmd
. '->insert(NULL, $form[' . $me->__word . ']->required ? \' <span class="req">*</span>\' : \'\')');
}