Multiplier: componentes dinámicos

Una herramienta para crear dinámicamente componentes interactivos.

Empecemos por un ejemplo típico: imagine una lista de productos en una tienda en línea donde quiere un formulario “Añadir al carrito” para cada artículo. Un enfoque posible es envolver todo el listado en un único formulario. Un método mucho más cómodo, sin embargo, lo ofrece Nette\Application\UI\Multiplier.

Multiplier le permite definir cómodamente una factory para varios componentes. Funciona según el principio de los componentes anidados: cualquier componente que herede de Nette\ComponentModel\Container puede contener otros componentes.

Vea el capítulo sobre el modelo de componentes de la documentación.

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

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;
	});
}

Ahora, en la plantilla, podemos renderizar sin más el formulario de 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} sigue un formato que significa:

  1. Obtener el componente shopForm.
  2. De él, obtener el hijo llamado $item->id.

Durante la primera llamada del punto 1, el componente shopForm aún no existe, así que se llama a su factory createComponentShopForm. Después, sobre el componente obtenido (una instancia de Multiplier) se llama a la factory del formulario concreto, que es la función anónima que pasamos al constructor de Multiplier.

En la siguiente iteración del bucle foreach ya no se llamará al método createComponentShopForm (porque el componente ya existe). Pero como buscamos un hijo distinto (ya que $item->id será distinto en cada iteración), la función anónima se llamará de nuevo y devolverá un formulario nuevo.

Solo queda asegurarse de que el formulario añada al carrito el producto correcto: por ahora, el formulario es idéntico para todos los productos. Aquí nos ayuda una característica de Multiplier (y, en general, de cualquier factory de componentes en Nette Framework): toda factory recibe como primer argumento el nombre del componente que se está creando. Además, una factory de Multiplier recibe como segundo argumento la propia instancia de Multiplier. En nuestro caso, el primer argumento será $item->id, que es justo la información que necesitamos. Así que basta con modificar ligeramente la creación del formulario:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function (string $itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('amount', 'Amount:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Add to cart');
		return $form;
	});
}
versión: 4.x