Multiplier: dynamiczne komponenty
*********************************
.[perex]
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 [api: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 [api:Nette\ComponentModel\Container] może zawierać inne komponenty.
.[tip]
Zobacz w dokumentacji rozdział o [modelu komponentów |components#Komponenty w głąb].
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:
```php
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.
```latte
{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:
```php
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;
});
}
```
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:
- Pobierz komponent
shopForm.
- 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;
});
}