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:
- obtenha o componente
shopForm
- 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;
});
}