Multiplicador: Componentes dinâmicos

Uma ferramenta para a criação dinâmica de componentes interativos

Vamos começar com um problema típico: temos uma lista de produtos em um site de comércio eletrônico e queremos acompanhar cada produto com um formulário add to cart. Uma das maneiras é envolver toda a lista em um único formulário. Uma maneira mais conveniente é usar Nette\Application\UI\Multiplier.

O multiplicador permite definir uma fábrica para múltiplos componentes. Ele se baseia no princípio de componentes aninhados – cada componente herdado de Nette\ComponentModel\Container pode conter outros componentes.

Veja o modelo dos componentes na documentação.

O multiplicador posa como um componente pai que pode criar dinamicamente seus filhos usando a callback passada no construtor. Veja o exemplo:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function () {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('amount', 'Amount:')
			->setRequired();
		$form->addSubmit('send', 'Add to cart');
		return $form;
	});
}

No modelo podemos apresentar um formulário para cada produto – e cada formulário será de fato um componente único.

{foreach $items as $item}
	<h2>{$item->title}</h2>
	{$item->description}

	{control "shopForm-$item->id"}
{/foreach}

Argumento passado para {control} tag diz:

  1. obter um componente shopForm
  2. e devolver seu filho $item->id

Durante a primeira chamada de 1. o componente shopForm ainda não existe, portanto o método createComponentShopForm é chamado para criá-lo. Uma função anônima passada como parâmetro para o Multiplicador, é então chamada e um formulário é criado.

Nas iterações subsequentes do foreach o método createComponentShopForm não é mais chamado porque o componente já existe. Mas como fazemos referência a outra criança ($item->id varia entre iterações), uma função anônima é chamada novamente e uma nova forma é criada.

A última coisa é garantir que o formulário realmente acrescente o produto correto ao carrinho porque no estado atual todos os formulários são iguais e não podemos distinguir a que produtos eles pertencem. Para isso, podemos usar a propriedade de Multiplicador (e em geral de qualquer método de fábrica de componentes em Nette Framework) que cada método de fábrica de componentes recebe o nome do componente criado como o primeiro argumento. Em nosso caso, isso seria $item->id, que é exatamente o que precisamos para distinguir os produtos individuais. Tudo o que precisamos fazer é modificar o código para criar o formulário:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function ($itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('amount', 'Amount:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Add to cart');
		return $form;
	});
}
versão: 4.0