Multiplier: dynamiczne komponenty

Narzędzie do dynamicznego tworzenia komponentów interaktywnych.

Zacznijmy od typowego przykładu: wyobraź sobie listę produktów w sklepie internetowym, przy której chcesz wyświetlić dla każdego artykułu formularz “Do koszyka”. Jedną z możliwości jest opakowanie całej listy w jeden formularz. O wiele wygodniejszy sposób oferuje jednak Nette\Application\UI\Multiplier.

Multiplier pozwala wygodnie zdefiniować fabrykę dla wielu komponentów. Działa na zasadzie komponentów zagnieżdżonych – każdy komponent dziedziczący po Nette\ComponentModel\Container może zawierać inne komponenty.

Zobacz w dokumentacji rozdział o modelu komponentów.

Istota Multipliera polega na tym, że działa jak rodzic, który potrafi dynamicznie tworzyć swoich potomków za pomocą callbacku przekazanego w konstruktorze. Zobacz przykład:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function () {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('amount', 'Ilość:')
			->setRequired();
		$form->addSubmit('send', 'Do koszyka');
		return $form;
	});
}

Teraz w szablonie możemy po prostu wyrenderować formularz dla każdego produktu – i każdy naprawdę będzie osobnym komponentem.

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

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

Argument przekazany w tagu {control} ma format, który oznacza:

  1. Pobierz komponent shopForm.
  2. Z niego pobierz potomka o nazwie $item->id.

Przy pierwszym wywołaniu punktu 1 komponent shopForm jeszcze nie istnieje, więc wywoływana jest jego fabryka createComponentShopForm. Następnie na uzyskanym komponencie (instancji Multipliera) wywoływana jest fabryka konkretnego formularza, czyli anonimowa funkcja, którą przekazaliśmy do konstruktora Multipliera.

W kolejnej iteracji pętli foreach metoda createComponentShopForm nie zostanie wywołana ponownie (bo komponent już istnieje). Ponieważ jednak szukamy innego potomka ($item->id będzie w każdej iteracji inne), anonimowa funkcja zostanie wywołana ponownie i zwróci nowy formularz.

Pozostaje już tylko zadbać o to, aby formularz dodawał do koszyka właściwy produkt – obecnie formularz jest identyczny dla każdego produktu. Pomaga nam w tym cecha Multipliera (i ogólnie każdej fabryki komponentów w Nette Framework): każda fabryka otrzymuje jako pierwszy argument nazwę tworzonego komponentu. Ponadto fabryka Multipliera otrzymuje jako drugi argument samą instancję Multipliera. W naszym przypadku pierwszym argumentem będzie $item->id, czyli dokładnie ta informacja, której potrzebujemy. Wystarczy więc nieznacznie zmodyfikować tworzenie formularza:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function (string $itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('amount', 'Ilość:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Do koszyka');
		return $form;
	});
}
wersja: 4.x