Multiplier: динамические компоненты
Инструмент для динамического создания интерактивных компонентов
Начнем с типичного примера: у нас есть список товаров в интернет-магазине, и для каждого мы хотим вывести форму для добавления товара в корзину. Один из возможных вариантов — обернуть весь список в одну форму. Однако гораздо более удобный способ предлагает нам Nette\Application\UI\Multiplier.
Multiplier позволяет удобно определить фабрику для нескольких компонентов. Он работает по принципу вложенных компонентов — каждый компонент, наследующий от Nette\ComponentModel\Container, может содержать другие компоненты.
См. главу о модели компонентов в документации или лекцию Яна Тврдика.
Суть Multiplier заключается в том, что он выступает в роли родителя, который может динамически создавать своих потомков с помощью callback-функции, переданной в конструкторе. См. пример:
protected function createComponentShopForm(): Multiplier
{
return new Multiplier(function () {
$form = new Nette\Application\UI\Form;
$form->addInteger('count', 'Количество товара:')
->setRequired();
$form->addSubmit('send', 'Добавить в корзину');
return $form;
});
}
Теперь мы можем в шаблоне просто для каждого товара отрисовать форму — и каждая будет действительно уникальным компонентом.
{foreach $items as $item}
<h2>{$item->title}</h2>
{$item->description}
{control "shopForm-$item->id"}
{/foreach}
Аргумент, переданный в теге {control}
, имеет формат, который
говорит:
- получи компонент
shopForm
- и из него получи потомка
$item->id
При первом вызове пункта 1. shopForm
еще не существует, поэтому
вызывается его фабрика createComponentShopForm
. На полученном компоненте
(экземпляре Multiplier) затем вызывается фабрика конкретной формы — это
анонимная функция, которую мы передали Multiplier в конструкторе.
В следующей итерации foreach метод createComponentShopForm
уже не будет
вызван (компонент существует), но поскольку мы ищем другого его потомка
($item->id
будет разным в каждой итерации), снова будет вызвана
анонимная функция и вернет нам новую форму.
Единственное, что остается, — это убедиться, что форма добавит в
корзину действительно тот товар, который нужно — в настоящее время
форма для каждого товара абсолютно одинакова. Нам поможет свойство
Multiplier (и вообще любой фабрики компонентов в Nette Framework), а именно то, что
каждая фабрика в качестве своего первого аргумента получает имя
создаваемого компонента. В нашем случае это будет $item->id
, что
является именно той информацией, которая нам нужна. Достаточно немного
изменить создание формы:
protected function createComponentShopForm(): Multiplier
{
return new Multiplier(function ($itemId) {
$form = new Nette\Application\UI\Form;
$form->addInteger('count', 'Количество товара:')
->setRequired();
$form->addHidden('itemId', $itemId);
$form->addSubmit('send', 'Добавить в корзину');
return $form;
});
}