Multiplier: динамични компоненти

Инструмент за динамично създаване на интерактивни компоненти

Да започнем с типичен пример: имаме списък със стоки в електронен магазин, като за всяка искаме да покажем формуляр за добавяне на стоката в количката. Един от възможните варианти е да обвием целия списък в един формуляр. Много по-удобен начин обаче ни предлага Nette\Application\UI\Multiplier.

Multiplier позволява удобно да се дефинира фабрика за множество компоненти. Работи на принципа на вложените компоненти – всеки компонент, наследяващ Nette\ComponentModel\Container, може да съдържа други компоненти.

Вижте главата за компонентния модел в документацията или лекцията от Honza Tvrdík.

Същността на 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}, е във формат, който казва:

  1. вземи компонента shopForm
  2. и от него вземи потомъка $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;
	});
}
версия: 4.0