Multiplier: dynamiczne komponenty
Narzędzie do dynamicznego tworzenia interaktywnych komponentów
Wyjdźmy od typowego przykładu: mamy listę towarów w sklepie internetowym, przy czym przy każdym będziemy chcieli wyświetlić formularz do dodania towaru do koszyka. Jedną z możliwych opcji jest opakowanie całego listingu w jeden formularz. Znacznie wygodniejszy sposób oferuje nam jednak Nette\Application\UI\Multiplier.
Multiplier umożliwia wygodne definiowanie fabryczki 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ć kolejne komponenty.
Zobacz rozdział o modelu komponentowym w dokumentacji lub wykład Honzy Tvrdíka.
Istotą Multipliera jest to, że występuje w pozycji rodzica, który potrafi dynamicznie tworzyć swoje potomstwo 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('count', 'Ilość towaru:')
->setRequired();
$form->addSubmit('send', 'Dodaj do koszyka');
return $form;
});
}
Teraz możemy w szablonie po prostu przy każdym towarze wyświetlić formularz – i każdy będzie rzeczywiście unikalnym komponentem.
{foreach $items as $item}
<h2>{$item->title}</h2>
{$item->description}
{control "shopForm-$item->id"}
{/foreach}
Argument przekazany w znaczniku {control}
jest w formacie, który mówi:
- pobierz komponent
shopForm
- a z niego pobierz potomka
$item->id
Przy pierwszym wywołaniu punktu 1. shopForm
jeszcze nie istnieje, więc wywołana zostanie jego fabryka
createComponentShopForm
. Na uzyskanym komponencie (instancji Multipliera) jest następnie wywoływana fabryka
konkretnego formularza – co jest anonimową funkcją, którą przekazaliśmy Multiplierowi w konstruktorze.
W kolejnej iteracji foreache już metoda createComponentShopForm
nie będzie wywoływana (komponent istnieje), ale
ponieważ szukamy jego innego potomka ($item->id
będzie w każdej iteracji inne), ponownie zostanie wywołana
anonimowa funkcja i zwróci nam nowy formularz.
Jedyne, co pozostaje, to zapewnić, aby formularz dodał do koszyka rzeczywiście ten towar, który ma – obecnie formularz
przy każdym towarze jest całkowicie identyczny. Pomoże nam właściwość Multipliera (i ogólnie każdej fabryki komponentu w
Nette Framework), a mianowicie ta, że każda fabryka jako swój pierwszy argument otrzymuje nazwę tworzonego komponentu. W
naszym przypadku będzie to $item->id
, co jest dokładnie tą informacją, której potrzebujemy. Wystarczy więc
lekko zmodyfikować tworzenie formularza:
protected function createComponentShopForm(): Multiplier
{
return new Multiplier(function ($itemId) {
$form = new Nette\Application\UI\Form;
$form->addInteger('count', 'Ilość towaru:')
->setRequired();
$form->addHidden('itemId', $itemId);
$form->addSubmit('send', 'Dodaj do koszyka');
return $form;
});
}