Přiohnutí makra {label /} pro vlastní účely

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
uestla
Backer | 799
+
0
-

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

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

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

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

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

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

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>\' : \'\')');
}