Multiplier: componentes dinámicos

Herramienta para la creación dinámica de componentes interactivos

Partamos de un ejemplo típico: tenemos una lista de productos en una tienda online, y para cada uno queremos mostrar un formulario para añadir el producto al carrito. Una de las posibles variantes es envolver todo el listado en un único formulario. Sin embargo, un método mucho más cómodo nos lo ofrece Nette\Application\UI\Multiplier.

Multiplier permite definir cómodamente una pequeña fábrica para múltiples componentes. Funciona según el principio de componentes anidados: cada componente que hereda de Nette\ComponentModel\Container puede contener otros componentes.

Vea el capítulo sobre el modelo de componentes en la documentación o la charla de Honza Tvrdík.

La esencia de Multiplier es que actúa como un padre que puede crear dinámicamente sus hijos mediante un callback pasado en el constructor. Vea el ejemplo:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function () {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Cantidad de productos:')
			->setRequired();
		$form->addSubmit('send', 'Añadir al carrito');
		return $form;
	});
}

Ahora podemos simplemente, en la plantilla, renderizar un formulario para cada producto, y cada uno será realmente un componente único.

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

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

El argumento pasado en la etiqueta {control} tiene un formato que indica:

  1. obtén el componente shopForm
  2. y de él obtén el hijo $item->id

En la primera llamada al punto 1., shopForm aún no existe, por lo que se llama a su fábrica createComponentShopForm. Sobre el componente obtenido (instancia de Multiplier) se llama entonces a la fábrica del formulario específico, que es la función anónima que pasamos a Multiplier en el constructor.

En la siguiente iteración del foreach, el método createComponentShopForm ya no será llamado (el componente existe), pero como buscamos a su otro hijo ($item->id será diferente en cada iteración), se volverá a llamar a la función anónima y nos devolverá un nuevo formulario.

Lo único que queda es asegurar que el formulario añada al carrito realmente el producto que debe; actualmente, el formulario es completamente idéntico para cada producto. Nos ayudará una propiedad de Multiplier (y en general de cada fábrica de componentes en Nette Framework), y es que cada fábrica recibe como primer argumento el nombre del componente que se está creando. En nuestro caso, será $item->id, que es exactamente el dato que necesitamos. Basta con modificar ligeramente la creación del formulario:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function ($itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Cantidad de productos:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Añadir al carrito');
		return $form;
	});
}
versión: 4.0