WebChemistry/Multiplier dynamicky vytvořený select + generované ID

Ajax
Člen | 59
+
0
-

Ahoj,

mám problém s WebChemistry/Multiplier. Jde o to, že formulář má identické řádky (kromě ID) a taky kolonku Jméno. No a taky má obsahovat select, ve kterém je seznam jmen z ostatních řádků formuláře. Seznam do selectu si vygeneruju, ale problém nastane, když uživatel přidá řádek před add funkci multiplieru. Nevím, jak se dostat ke kolonce, kterou uživatel vyplnil v dynamicky generovaném řádku. Potřeboval bych něco jako onAddCallback. S tím souvisí i to, že potřebuju do každého řádku přidat unikátní ID do hidden pole. Zde je kód:

Továrna formuláře, $this->config jsou data z CSV, $this->names je pole, které si vygeneruju foreachem a které bych potřeboval doplnit o input, který mi dá uživatel v poli name (pole je vygenerované přes addCreateButton). Také bych potřeboval poslední požité ID, které může být taky dynamické.

class RegisterConfigFormFactory
{

	public function create() {
		$form = new Form;

		$multiplier = $form->addMultiplier('registerConfigForm', function (Nette\Forms\Container $container, Nette\Forms\Form $form) {
			$container->addText('item_id', 'ID')
				->setRequired('Prosím, vyplňte jméno');

			$container->addText('name', 'Jméno')
				->setRequired('Prosím, vyplňte jméno');

			$container->addSelect('parent_id', 'Rodič', $this->names)
				->setPrompt('Vyberte rodiče');
		});

		$multiplier->setValues($this->config);

		$multiplier->addCreateButton('Přidat');
		$multiplier->addRemoveButton('Odstranit');

		$form->addSubmit('save', 'Uložit');

		return $form;
	}

	public function setConfiguration(array $config) {
		$names = [];
		foreach($config as $seq => $row) {
			$names[$row['item_id']] = $row['name'];
		}

		// Zde bych potřeboval obsah všech inputu z names, i těch přidaných pomocí createButton
		$this->names = $names;

		$this->config = $config;
	}
}

Presenter:

	public function createComponentRegisterConfigForm() {
		$this->redrawControl();
		$form = $this->registerConfigFormFactory->create();

		$form->onSuccess[] = function(Form $form, $values) {
			dd($values);
			$this->redrawControl();
		};

		return $form;
	}

Šablona:

		{form registerConfigForm, class => 'ajax'}
			<ul class="errors" n:if="$form->hasErrors()">
				<li n:foreach="$form->errors as $error">{$error}</li>
			</ul>

			<table id="configItemsTab">
				<thead>
				<tr>
					<th>ID</th>
					<th>Jméno</th>
					<th>Rodič</th>
					<th></th>
				</tr>
				</thead>
				<tbody>
				<tr n:multiplier="registerConfigForm">
					<td>{input item_id , class => 'item_id'}</td>
					<td>{input name}</td>
					<td>{input parent_id}</td>
					<td>{btnRemove 'class' => 'ajax'}</td>
				</tr>
				</tbody>
			</table>

			{btnCreate registerConfigForm class => ajax}

			{input 'save', 'class' => 'ajax'}
		{/form}

Je to takové kostrbaté, snad to pochopíte.. Dá se toho nějak dosáhnout?

Editoval Ajax (28. 3. 2018 17:14)

Martk
Člen | 661
+
0
-

Neměl jsem čas na hledání optimálního řešení, klidně se poděl o svůj návrh, ale asi takto:

	protected $config = [
		'foo' => 'foo',
		'bar' => 'bar',
	];

	private $filled = false;

	protected function fillConfig(Nette\Application\UI\Form $form) {
		if (!$this->filled) {
			$values = $form->getValues();
			foreach ($values['multiplier'] as $value) {
				$this->config[] = $value['name'];
			}
			$this->filled = true;
		}
	}

	protected function createComponentMultiplier() {
		$form = new Nette\Application\UI\Form();

		$form['multiplier'] = $multiplier = new Multiplier(function (Nette\Forms\Container $container) {
			$container->addText('name', 'Name');
			$container->addSelect('select', 'Select', $this->config);
		});

		$form['multiplier']->onCreate[] = function (Nette\Forms\Container $container) {
			if ($container->getForm()->isSubmitted()) {
				$this->fillConfig($container->getForm());
			}

			$container['select']->setItems($this->config);
		};

		$multiplier->addRemoveButton('Remove');
		$multiplier->addCreateButton('Create');

		$form->addSubmit('send');

		return $form;
	}

Musíš si stáhnout nejnovější dev verzi

Ajax
Člen | 59
+
0
-

WOW! To jsem nečekal. Děkuju moc!!

Select funguje, ale ID nějak nemůžu rozchodit. Když jsem dal

	$form['registerConfigForm']->onCreate[] = function (Nette\Forms\Container $container) {
			$container['parent_id']->setItems($this->names);
			$container['item_id']->setValue('9999');
		};

Tak to naplní všechny, kromě posledního (přidávaného) řádku. Přitom setItems() hned vedle funguje dobře a podle očekávání. Kde je chyba?

Jen jedna poznámka, nestálo by za to, aby add button nevalidoval formulář? Nebo aspoň dát možnost zavolat setValidationScope(FALSE)

Martk
Člen | 661
+
0
-

Jestli máš podobný kód jako já, tak to bude tím, že se odesílají jen předchozí inputy, ten nový ještě ne, ale jeho hodnotu v průběhu kódu znáš, takže jí můžeš snadno přidat, podle kódu je to hodnota ‚9999‘.

protected function fillConfig(Nette\Application\UI\Form $form) {
    if (!$this->filled) {
        $values = $form->getValues();
        foreach ($values['multiplier'] as $value) {
            $this->config[] = $value['name'];
        }
		$this->config[] = $myDefaultValue;
        $this->filled = true;
    }
}

Defaultně je setValidationScope jen u remove buttonu, protože se tam nic nepotřebuje validovat. U add buttonu můžeš snadno upravovat co potřebuješ:

$multiplier->addCreateButton('Create', 1, function (SubmitButton $submitter) {
	$submitter->setValidationScope(false);
});
Ajax
Člen | 59
+
0
-

No, o to nejde jak tu hodnotu zjistit, problem je, že nejde nastavit. Ty devítky byl jen příklad. Můj úkol je nastavit přidávanému novému řádku do inputu item_id unikátní hodnotu. Myslel jsem, že stačí zavolat v onCreate callbacku $container['item_id']->setValue($uniqueId);, ale to mi nefunguje. Nastaví to všechny řádky kromě toho nového…

ValidationScope funguje, to jsem přehlédl, děkuji.

Martk
Člen | 661
+
0
-

Mělo by to stačit dát v multiplieru jako defaultValue

$container->addText('item_id', 'ID')
	->setDefaultValue('9999');

Ale tu chybu v onCreate ještě prověřím.

Ajax
Člen | 59
+
0
-

To je právě problém, já tu hodnotu neznám, musím ju nastavit jako max($usedIds) + 1, takže mám

	protected function fillConfig(Nette\Forms\Form $form) {
		if (!$this->filled) {
			$this->names = [];
			$this->ids = [];
			$values = $form->getValues();
			foreach ($values['registerConfigForm'] as $value) {
				$this->names[$value['item_id']] = $value['name'];
				$this->ids[] = $value['item_id'];
			}

			$this->filled = true;
		}
	}

no a teď bych ho potřeboval nastavit max($this->ids) + 1 poli v přidávaném řádku.

Martk
Člen | 661
+
0
-

Musím ještě trošku přepsat třídu, aby se event onCreate volal i pro přidaný container. Ještě k tomu vyřešit související issue na gitu.

Můžeš založit issue na gitu, abys měl přehled, kdy to bude opraveno, předpokládám někdy v tomto týdnu.

Ajax
Člen | 59
+
0
-