Snippety uvnitř forumláře

monty
Člen | 66
+
0
-

Ahoj.

Lze nějakým způsobem obalit konkrétní formulářový prvek do snippetu bez toho, aniž bych musel formulář manuálně překreslovat?

Díky
Peťa

Šaman
Člen | 2640
+
0
-

Ne (resp. nevím o tom). Ale můžeš si upravit (nebo napsat vlastní) renderer.

monty
Člen | 66
+
0
-

Šaman napsal(a):

Ne (resp. nevím o tom). Ale můžeš si upravit (nebo napsat vlastní) renderer.

No, popravdě jsem to už zkoušel, ale nepodařilo se mi přijít na to, jak se přes renderer dostat přímo ke control prvku.
Myslel jsem tedy, že přes něj můžu ovlivnit jen vykreslování nadřazených kontejnerů.

Mohl bys mě prosím trochu nasměrovat?

monty
Člen | 66
+
0
-

Teď jsem si přečetl, co jsem napsal a jen pro upřesnění, protože to zní dost nejasně :)
Není mi totiž jasné, jak přes renderer zjistit „name“ prvku, abych pak mohl snippetu přidělit unikátní ID.

Šaman
Člen | 2640
+
0
-

Plánuji si to do rendereru zakomponovat, ale zatím aspoň pro představu: takhle si přidávám bootstrap přílepky (zleva nebo zprava) k inputu.

<?php
public function renderControl(Nette\Forms\IControl $control)
	{
		$this->controlsInit();


		$inputGroup = $this->getWrapper('input-group container');

		if ($control->getOption('input-group-prefix')) {
			$prefix = '<div class="input-group-addon">' . $control->getOption('input-group-prefix') . '</div>';
		}
		elseif ($control->getOption('input-group-prefix-glyphicon')) {
			$prefix = '<div class="input-group-addon">'
					. '<span class="glyphicon glyphicon-'
					. $control->getOption('input-group-prefix-glyphicon')
					. '" aria-hidden="true"></span>'
					. '</div>';
		}
		else {
			$prefix = '';
		}

		if ($control->getOption('input-group-suffix')) {
			$suffix = '<div class="input-group-addon">' . $control->getOption('input-group-suffix') . '</div>';
		}
		elseif ($control->getOption('input-group-suffix-glyphicon')) {
			$suffix = '<div class="input-group-addon">'
					. '<span class="glyphicon glyphicon-'
					. $control->getOption('input-group-suffix-glyphicon')
					. '" aria-hidden="true"></span>'
					. '</div>';
		}
		else {
			$suffix = '';
		}
		.
		.
		.
		if (!empty($prefix) || !empty($suffix)) {
			$el = $inputGroup->setHtml($prefix . $el . $suffix);
		}
		return $body->setHtml($el . $this->renderErrors($control));
?>

A použití je:

<?php
$form->addText('personalId', 'Osobní číslo')->setOption('input-group-prefix', "foo");
?>

Pro obalení samotného controlu snippetem to bude skoro stejné. To jméno snippetu bych negeneroval automaticky z názvu prvku (i když i ten by měl jít dohledat), ale pomocí nějaké vlastní option ->setOption('snippet', "fooBar")

Editoval Šaman (16. 1. 2018 0:01)

monty
Člen | 66
+
0
-

@Šaman
To je paráda, chystám se řešit úplně to samý :)
Takže díky, zkusím zakomponovat tvůj návrh.

Snippety nad control řeším kvůli dynamickému selectu, abych nemusel překreslovat celý formulář po načtení jeho hodnot z databáze. Protože po překreslení přijde uživatel o už zadané hodnoty.

Zatím jsem to ale vyřešil takovou „oklikou“, před ajaxem si načtu data a po načtení je zase do inputů vrátím.

No a jméno snippetu jsem chtěl generovat automaticky, protože v aplikaci budu muset určitě použít nějaký replikátor a tam zadávat jméno snippetu ručně nebudu určitě. Ale to budu řešit, až k tomu dojde a napadá mě, že by to snad v presenteru nějak napůl generovat šlo.

Přišel jsem teď ale na zásadnější problém.
Po překreslení snippetu, ve kterém je formulář, se po kliknutí na submit formulář neodešle.
Resp. ajax request proběhne, ale očividně se nezavolá funkce onSubmit.

Nesetkal si se s tím?
Hledal jsem na foru a ptal se Googla, ale nic, co by mi pomohlo jsem nenašel.

Šaman
Člen | 2640
+
0
-

Aha, nesetkal, mě zase přestal fungovat ajax. Takže se to odešle, zpracuje, ale neajaxově. Zkouším s tím pohnout, kdyžtak dám vědět.

monty
Člen | 66
+
0
-

Tak to mě mrzí, že máme takový trable s formulářema, s onError bohužel zatím vůbec nepracuju.

Ta moje chyba ajaxem není.
Zjistil jsem, že i když forumlář neodesílám přes ajax, tak po překreslení jeho snippetu se po kliknutí na submit jen refreshne stránka.

Tak pokud něco zjistíš, prosím napiš, já se bez tohodle nehnu :/
Zkusím ještě hodit dotaz do začátečníků, protože tam prozatím spadám :)

Šaman
Člen | 2640
+
0
-

Zkus si tenhle minimalizovaný příklad se závislými inputy.
Nenapadá mě moc kde by mohla ztratit obsluha onSuccess. Navázaný ajax můžeš při překreslení ztratit proto, že nový (překreslený) prvek už není TEN prvek, na který jsi navázal událost. Ale samotné neajaxové odeslání formuláře by mělo být bez problémů, pokud neměníš ID, případně NAME atribut odesílacího tlačítka. Server se ani nedozví, že se formulář na klientu překreslil.

monty
Člen | 66
+
0
-

No v tomhle příkladu je právě ve snippetu obalený přímo ten input.
To je právě to, čeho se snažím docílit, ale bez toho, aniž bych musel manuálně vykreslovat.

Zkusím ještě pozměnit ten renderer, podle tvýho návrhu.

Tím by se pak asi zabili dvě mouchy jednou ranou, protože by se nepřekresloval celý formulář a tím pádem bude fungovat submit.
Pokud se mi to s tím rendererem nepovede, napadla mě ještě varianta, že si budu podle zadání načítat ajaxem JSON a z toho si pak překreslím select čistě pomocí JS.

David Matějka
Moderator | 6445
+
0
-

ahoj, snippety bez manualniho renderovani myslim nijak (snadno) nezprovoznis. snippety jsou uzce provazany s latte

monty
Člen | 66
+
0
-

@DavidMatějka
Ahoj Davide.
Prima, díky za objasnění.

Prosím tě, jsi jeden z nejpovolanějších, takže určitě budeš vědět.
Existuje možnost, pokud budu chtít dynamicky přidávat a odebírat prvky formuláře pomocí JS, jak je při tom přes handle inicializovat/odpojovat tak, aby všechno fungovalo jak má a Nette je viděl nebo s nima případně nepočítal?
Nebo to bez nějakého replikátoru prostě řešit nejde?

David Matějka
Moderator | 6445
+
0
-

@monty muzes jit cestou low level formularu, kdy pri zpracovani obchazis nejake validacni mechanismy nette. ale nejaky replikator je obvykle lepsi cesta.

Šaman
Člen | 2640
+
0
-

Tak potvrzuji, že renderer na snippety nejspíš jednoduše nejde. Dokážu si vyrenderovat ať už <div n:snippet="foo"> okolo inputu, nebo <div id="snippet--foo">, výsledné html je identické, ale v přeložené šabloně (v cache) chybí obsluha těchto snippetů (vygenerovaná function blockFoo($_args)).
A to jsem neřešil dynamicky vygenerovaná pole formuláře, ale přímo ručně řečené, které inputy chci obalit snippetem.

Editoval Šaman (16. 1. 2018 16:45)

monty
Člen | 66
+
0
-

@Šaman @DavidMatějka
Tak vám oběma moc děkuju za reakce, jdu se s tím nějak poprat :)

Mějte se

monty
Člen | 66
+
0
-

Nuže.
Vykreslil jsem si manuálně formulář, obalil jsem select snippetem a stále ta samá chyba…submit…refresh…nic.
Dle rady @Mortisson jsem přidal onError a ejhle, přece jen něco…„This field is required.“ a po dumpu hodnot, jsem zjistil, že překreslený select je podle všeho null.

Jsem už z toho úplně tumpachovej a nejradši bych utekl někam do jeskyně.

Můžete na to prosím Vás někdo hodit oko?
Nahrál jsem to všechno na GIT.

Presenter
Latte

Šaman
Člen | 2640
+
0
-

Vždyť v tom handleru nenastavuješ values toho formuláře. To, že je vykreslíš neznamená, že ty hodnoty fomulář zná. Přijdou mu nějaké neznámé hodnoty a on je zahodí jako podvržené.
Buď to tedy udělej jako v tom mém příkladu, ten funguje.
Anebo to udělej přes low-level formuláře, které ti odkazoval David. To je totiž přesně to, co děláš (vykreslíš v šabloně inputy, které objekt $form nezná, takže je neumí zpracovat).

monty
Člen | 66
+
0
-

To mi ale nejde do hlavy.
V tom handleru si přece nastavuju class property, podle který se i nastavují prvky formuláře (výchozí radio a hodnoty selectu).

Na tvůj příklad jsem se koukal.
Zkoušel jsem i v handleru nastavit hodnoty pro select pomocí setItems(), ale i tak to nešlo.
Napadá mě ale, že to bylo možná tou property se kterou i nadále továrnička pracovala.
Zkusím to večer ještě překopat.

Matouš Trča
Člen | 1
+
0
-

Ahoj, nakonec jsem vymyslel následující workaround, pokud to ještě někoho zajímá. Nepoužívá se redrawControl, pouze se simuluje chování této metody. Lepší fungující řešení jsem na fóru nenašel.

public function handleChange(): void
 {
     $fileSelect = $this['articleForm']
         ->getComponent('files')
         ->setItems([...]);
     $this->payload->snippets = [
         $fileSelect->getHtmlId() => (string) $fileSelect->getControl()
     ];
 }