Multiplier: Динамические компоненты
Инструмент для динамического создания интерактивных компонентов.
Давайте начнем с типичной проблемы: у нас есть список товаров на сайте электронной коммерции, и мы хотим сопроводить каждый товар формой добавить в корзину. Один из способов — обернуть весь листинг в одну форму. Более удобным способом является использование Nette\Application\UI\Multiplier.
Multiplier позволяет определить фабрику для нескольких компонентов. Он основан на принципе вложенных компонентов — каждый компонент, наследующий от Nette\ComponentModel\Container, может содержать другие компоненты.
См. модель компонента в документации.
Multiplier представляет собой родительский компонент, который может динамически создавать свои дочерние компоненты, используя обратный вызов, переданный в конструкторе. См. пример:
protected function createComponentShopForm(): Multiplier
{
return new Multiplier(function () {
$form = new Nette\Application\UI\Form;
$form->addInteger('amount', 'Amount:')
->setRequired();
$form->addSubmit('send', 'Add to cart');
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,
и создается форма.
В последующих итерациях foreach
метод createComponentShopForm
больше
не вызывается, поскольку компонент уже существует. Но поскольку мы
ссылаемся на другого потомка ($item->id
варьируется между
итерациями), анонимная функция вызывается снова и создается
новая форма.
Последнее, что нужно сделать, это убедиться, что форма действительно
добавляет правильный товар в корзину, потому что в текущем состоянии
все формы равны, и мы не можем различить, к каким продуктам они
принадлежат. Для этого мы можем использовать свойство класса Multiplier (и
вообще любого метода фабрики компонентов в фреймворке Nette), что каждый
метод фабрики компонентов получает имя созданного компонента в
качестве первого аргумента. В нашем случае это будет $item->id
, что
именно то, что нам нужно, чтобы различать отдельные товары. Всё, что вам
нужно сделать, это изменить код для создания формы:
protected function createComponentShopForm(): Multiplier
{
return new Multiplier(function ($itemId) {
$form = new Nette\Application\UI\Form;
$form->addInteger('amount', 'Количество:')
->setRequired();
$form->addHidden('itemId', $itemId);
$form->addSubmit('send', 'Добавить в корзину');
return $form;
});
}