Mnożnik: składniki dynamiczne

Narzędzie do dynamicznego tworzenia interaktywnych komponentów

Zacznijmy od typowego przykładu: miejmy listę przedmiotów w sklepie internetowym, a dla każdego przedmiotu chcemy wypisać formularz dodawania przedmiotu do koszyka. Jedną z możliwych opcji jest zawinięcie całego listingu w jeden formularz. Jednak znacznie wygodniejszy sposób oferuje Nette\Application\UI\Multiplier.

Mnożnik pozwala na wygodne zdefiniowanie fabryki dla wielu komponentów. Działa na zasadzie zagnieżdżonych komponentów – każdy komponent dziedziczący po Nette\ComponentModel\Container może zawierać inne komponenty.

Zobacz rozdział o modelu komponentów w dokumentacji lub wykład Honzy Tvrdíka.

Istotą Multiplier jest to, że działa jako rodzic, który może dynamicznie tworzyć swoje dzieci za pomocą wywołania zwrotnego przekazanego w konstruktorze. Zobacz przykład:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function () {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Počet zboží:')
			->setRequired();
		$form->addSubmit('send', 'Přidat do košíku');
		return $form;
	});
}

Teraz możemy po prostu mieć formularz renderowany dla każdego elementu w szablonie – i każdy będzie naprawdę unikalnym komponentem.

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

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

Argument przekazywany w znaczniku {control} ma format mówiący:

  1. zdobądź komponent shopForm
  2. i uzyskać od niej dziecko $item->id

Przy pierwszym wywołaniu punktu 1., shopForm jeszcze nie istnieje, więc wywoływana jest jego fabryka createComponentShopForm Fabryka danej formy – którą jest anonimowa funkcja, którą przekazaliśmy Multiplierowi w konstruktorze – jest następnie wywoływana na uzyskanym komponencie (instancji Multiplera).

W kolejnej iteracji foreache metoda createComponentShopForm nie zostanie już wywołana (komponent istnieje), ale ponieważ szukamy jego drugiego potomka ($item->id będzie inny w każdej iteracji), funkcja anonimowa zostanie ponownie wywołana i zwróci nowy formularz.

Pozostaje tylko zadbać o to, aby formularz faktycznie dodawał do naszego koszyka przedmioty, które posiada – obecnie formularz jest dokładnie taki sam dla każdego przedmiotu. Cechą Multiplier (i w ogóle każdej fabryki komponentów w Nette Framework), która nam pomaga jest to, że każda fabryka dostaje jako pierwszy argument nazwę tworzonego komponentu. W naszym przypadku będzie to $item->id, czyli dokładnie taka informacja, jakiej potrzebujemy. Musimy więc tylko lekko zmodyfikować tworzenie formularza:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function ($itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Počet zboží:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Přidat do košíku');
		return $form;
	});
}
wersja: 4.0