Multiplier: componentes dinâmicos

Ferramenta para criação dinâmica de componentes interativos

Vamos partir de um exemplo típico: temos uma lista de produtos em uma loja virtual, e para cada um queremos exibir um formulário para adicionar o produto ao carrinho. Uma das opções possíveis é envolver toda a listagem em um único formulário. No entanto, um método muito mais conveniente nos é oferecido pelo Nette\Application\UI\Multiplier.

O Multiplier permite definir convenientemente uma pequena fábrica para múltiplos componentes. Funciona com base no princípio de componentes aninhados – cada componente que herda de Nette\ComponentModel\Container pode conter outros componentes.

Veja o capítulo sobre o modelo de componentes na documentação ou a palestra de Honza Tvrdík.

A essência do Multiplier é que ele atua na posição de pai, que pode criar seus descendentes dinamicamente usando um callback passado no construtor. Veja o exemplo:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function () {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Quantidade de produtos:')
			->setRequired();
		$form->addSubmit('send', 'Adicionar ao carrinho');
		return $form;
	});
}

Agora podemos, no template, simplesmente deixar renderizar o formulário para cada produto – e cada um será realmente um componente único.

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

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

O argumento passado na tag {control} está em um formato que diz:

  1. obtenha o componente shopForm
  2. e dele obtenha o descendente $item->id

Na primeira chamada do ponto 1., shopForm ainda não existe, então sua fábrica createComponentShopForm é chamada. No componente obtido (instância do Multiplier), a fábrica do formulário específico é então chamada – que é a função anônima que passamos para o Multiplier no construtor.

Na próxima iteração do foreach, o método createComponentShopForm não será mais chamado (o componente existe), mas como estamos procurando por um descendente diferente dele ($item->id será diferente em cada iteração), a função anônima será chamada novamente e nos retornará um novo formulário.

A única coisa que resta é garantir que o formulário adicione ao carrinho realmente o produto que deve – atualmente, o formulário é completamente idêntico para cada produto. A propriedade do Multiplier (e geralmente de cada fábrica de componentes no Nette Framework) nos ajudará, que é que cada fábrica recebe como seu primeiro argumento o nome do componente sendo criado. No nosso caso, será $item->id, que é exatamente a informação que precisamos. Basta, portanto, ajustar ligeiramente a criação do formulário:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function ($itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Quantidade de produtos:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Adicionar ao carrinho');
		return $form;
	});
}
versão: 4.0