Dynamicky pocet poli

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
martincohen
Člen | 14
+
0
-

Zdrastvujte,

chcek do formularu pridat podporu pre labels, velmi podobnu tej z Google Code Issue Trackeru:

http://code.google.com/…issues/entry

Ergo, formular ma zobraznenych 6 poli pre „Labels“. Uzivatel, ale moze kliknut na „Add Row“ a pridaju sa dalsie 3. Mam si na to vytvorit vlastny FormControl, alebo to tam niekde je?

Editoval martincohen (4. 6. 2009 17:29)

Honza Marek
Člen | 1664
+
0
-

Co já vim, tak to nette nepodporuje.

David Grudl
Nette Core | 8228
+
0
-

Něco takového chystám do Nette v brzké době přidat.

miira
Člen | 13
+
0
-

Neví někdo jak to s dynamickým přidáváním polí vypadá? Zaregistroval jsem, že proběhl „velký update formulářů“, nemohl by mě někdo popostrčit kudy do toho? :)

DocX
Člen | 154
+
0
-

miira napsal(a):

Neví někdo jak to s dynamickým přidáváním polí vypadá? Zaregistroval jsem, že proběhl „velký update formulářů“, nemohl by mě někdo popostrčit kudy do toho? :)

Asi bych využil kombinaci JS + AJAX + SESSION

miira
Člen | 13
+
0
-

DocX napsal(a):

Asi bych využil kombinaci JS + AJAX + SESSION

Díky za reakci, bohužel bych asi potřeboval něco konkrétnějšího. Není mi totiž jasné v jakém části životního cyklu a jak ta pole přidávat. Stačí něco jako

<script type="text/javascript">
	document.write('<input type text name="myName" />')
</script>

s tím, že formulář sám z postu pozná (což se mi moc nezdá, neboť by tam kdokoliv mohl podstrčit
cokoliv), že mu přibylo pole nebo je potřeba něco sofistikovanějšího.

DocX
Člen | 154
+
0
-

Nene. Chtěl jsi jen pošťouchnout :) Ale myslel jsem to nějak tak, že by se formulář normálně tvořil v PHP pomocí Nette\Forms. Ale měl by jsi v session nějakou informaci o tom, jaké prvky tam jsou a v té továrničcce při tvoření formuláře použít nějaký cyklus apod, prostě aby to vygenerovalo víc prvků. Na formuláři by jsi měl tlačítko „přidat položku“ (s vypnutou validací), které by v PHP připsalo něco k té session (a asi by musela i rovnou upravit ten formulář, protože už by byl vytvořen). AJAX + snippets v Nette by se postaraly o to, že se na stránce formulář aktualizuje.

To by byla inspirace, tady najdeš jak by jsi to mohl udělat:

Snad jsi mě pochopil :)

miira
Člen | 13
+
0
-

Už tuším kudy do toho, moc to pomohlo, díky.

Jen jsem doufal, že to s těmi přepracovanými formuláři bude o něco jednodušší :-)

Vitek Jezek
hledá kolegy | 285
+
0
-

da se to ted resit nejak sikovnejc, nez si udelat cizi HiddenPole, kam si budu javascriptem ukladat pocet prvku (tj prvky budu vytvaret dle cisla v tomto poli)? Davide, nastinis nam nejak tve reseni? ; )

//ukladat do session me nepripada moc pekne

Editoval Whitek (12. 10. 2009 23:22)

Endrju
Člen | 147
+
0
-

Ahoj, je nějaký pokrok v této oblasti?

David o pár řádků výše psal, že se na něco takového chystá, ale to bylo téměř před rokem. Nevíte, zda něco takového už vzniklo? Potřeboval bych dynamicky přidávat/odebírat inputy ve formuláři..


Zkoušel jsem něco takového sám (pomocí session), ale teď když se na to zpětně dívám, tak čert aby se v tom vyznal :).

Přidává to a odebírá inputy, ale odešle se vždy jen takový počet inputů, který jsem ve formuláři definoval (bez ohledu na to, že ten počet pak dynamicky změním). Nevím zda se musí ta formulářová komponenta nějak zaktualizovat pro daný počet prvků.. Nechápu.

Dám k nahlédnutí kód a třeba vás to někoho inspiruje a přijdete s nějakým řešením. (Jen podotýkám, že tam je možná pár řádků navíc, které momentálně nic nedělají).

Jak už jsem předeslal – je to s pomocí session a taky je dobré říct, že je to bez AJAXu (tedy chci, aby to fungovalo i bez podpory JavaScriptu)

Třída by měla poskytnout administraci pro vytváření/editaci ankety (konkrétně té od Ondřeje Brejly Poll control)

class Admin_PoolPresenter extends Admin_SecuredBasePresenter
{

	public function renderDefault()
	{
		// jestlize nebyl formular odeslan pomoci tlacitek pro pridani/odebrani inputu
		if (!$this['poolForm']['answerAdd']->isSubmittedBy() && !$this['poolForm']['answerDel']->isSubmittedBy()) {
			$session = Environment::getSession('poolForm');
			unset($session->poolForm); // tak vymazeme hodnoty ze session
		}
	}

	public function renderEdit($id = 0)
	{
		// jestlize nebyl formular odeslan pomoci tlacitek pro pridani/odebrani inputu
		if (!$this['poolForm']['answerAdd']->isSubmittedBy() && !$this['poolForm']['answerDel']->isSubmittedBy()) {
			$session = Environment::getSession('poolForm');
			unset($session->poolForm); // tak vymazeme hodnoty ze session
		}
		else {
			$pool = new PollControlModel;

			$question = $pool->getQuestion($id);
			if (!$question) {
				throw new BadRequestException('Záznam nenalezen');
			}

			$answers = $pool->getAnswers($id);
			if (!$answers) {
				throw new BadRequestException('Záznamy nenalezeny');
			}

			$session = Environment::getSession('poolForm');
			$session->poolForm['question'] = $question;


			$session->poolForm = $this['poolForm']->setDefaults($row);

			$this->redirect('edit');
		}
	}


	public function handleAddAnswer()
	{
		$this->invalidateControl('poolForm');

		$session = Environment::getSession('poolForm');
		$session->poolForm = $this['poolForm']->getValues();
		$session->poolForm['answersCount']++;

		if(!$this->isAjax()) {
			$this->redirect('this');
		}

	}

	public function handleDeleteAnswer()
	{
		$this->invalidateControl('poolForm');

		$session = Environment::getSession('poolForm');
		$session->poolForm = $this['poolForm']->getValues();
		unset($session->poolForm['answer'.$session->poolForm['answersCount']]);
		$session->poolForm['answersCount']--;

		if(!$this->isAjax()) {
			$this->redirect('this');
		}
	}

	protected function createComponentPoolForm()
	{
		$form = new AppForm();

		// nacteme data formulare ze session
		$session = Environment::getSession('poolForm');

		if (isset($session->poolForm['answersCount'])) {
			$answersCount = $session->poolForm['answersCount'];
		}
		else {
			$answersCount = 2; // defaultni pocet odpovedi
		}

		$form->addHidden('answersCount')->setValue($answersCount);

		$form->addText('question', 'Otázka:')
				->addRule(Form::FILLED, 'Vložte otázku.')
				->getControlPrototype()->class('poolInput');

		$form->addSubmit('answerAdd', 'Přidat odpoveď')
			->setValidationScope(NULL)
			->onClick[] = array($this, 'handleAddAnswer');
		$form->addSubmit('answerDel', 'Ubrat odpoveď')
			->setValidationScope(NULL)
			->onClick[] = array($this, 'handleDeleteAnswer');

		if($answersCount <= 2) {
			$form['answerDel']->setDisabled(TRUE);
		}

		for ($i = 1; $i <= $answersCount; $i++) {
			$form->addText('answer'.($i), 'Odpoveď #'.($i).':')
					->addRule(Form::FILLED, 'Vložte odpověď #'.($i).'.')
					->getControlPrototype()->class('poolInput');
		}

		$form->addSubmit('save', 'Uložit');
		$form->addSubmit('cancel', 'Zrušit')->setValidationScope(NULL);

		$form->addProtection('Prosím odešlete přihlašovací údaje znova (vypršla platnost tzv. bezpečnostního tokenu).');
		$form->onSubmit[] = callback($this, 'poolFormSubmitted');

		$form->getRenderer()->setClientScript(new LiveClientScript($form));

		// nastavime formulari hodnoty ze session
		if (isset($session->poolForm)) {
			$form->setDefaults($session->poolForm);
		}

		return $form;
	}



	public function poolFormSubmitted($form)
	{
		if ($form['save']->isSubmittedBy()) {
			$id = (int) $this->getParam('id');
			$poolModel = new PoolModel();

			$values = $form->getValues();

			// ulozime do DB otazku, do promenne si ulozime jeji ID
			$questionId = $poolModel->insertQuestion(array('question' => $values['question']));

			// ulozime odpovedi
			for ($i = 1; $i <= $values['answersCount']; $i++) {
				$poolModel->insertAnswer(array('questionId' => $questionId, 'answer' => $values['answer'.$i], 'votes' => 0));
			}

			$this->flashMessage('Anketa byla vytvořena.', 'updated');
		}

		$session = Environment::getSession('poolForm');
		unset($session->poolForm); // vymazeme hodnoty ze session

		$this->redirect('default');
	}

}

V metode createComponentPoolForm() definuji výchozí počet odpovědí (inputů) v anketě $answersCount = 2; a dle tohoto čísla se pak generuje patřičný počet inputů ve formuláři. Tohle naštěstí funguje, ale v metodě poolFormSubmitted($form), která zpracovává formulář se dostanou vždy jen první dva inputy (viz. $answersCount = 2), i když si jich tlačítky nacvakám třeba 5. Odešlou se prostě vždcky jen dva.

Podíváte se na to někdo? :)

Ola
Člen | 385
+
0
-

Dělá to kód v renderDefault mazající obsah session, který se totiž provede vždy (nevím ale, jak v ajaxu) – protože v metodách handle*** je redirect na action=default.

Editoval Ola (29. 6. 2010 23:14)

Endrju
Člen | 147
+
0
-

Ola napsal(a):

Dělá to kód v renderDefault mazající obsah session, který se totiž provede vždy (nevím ale, jak v ajaxu) – protože v metodách handle*** je redirect na action=default.

Nějak tomu asi nerozumím. Co konkrétně, že způsobuje kód v renderDefault? To že se mi nepřenáší všechny hodnoty?

Jak to chápu já.

  • V továrně vygeneruju input pro otázku, dvě tlačítka pro přidání a odebrání odpovědi a v továrně taky definuju, že se mají vykreslit minimálně dva inputy pro odpovědi.
  • Tlačítky přidat/odebrat odpověď volám metody handle***, ve kterých vždy do session uložím hodnoty z formuláře a zvýším/snížím čítač (číslo podle nejž se generuje určitý počet inputů pro odpovědi).

Jak s tím souvisí metoda renderDefault()? V té, pokud nechci, aby se mi při novém zobrazení formuláře naplnil formulář starými hodnotami ze session, tak session smažu. A to pouze tehdy pokud formulář nebyl odeslán tlačítky pro přidání/odebrání inputu.

Když tedy mám vygenerovaný formulář a vyrobených řekněme 5 inputů pro odpovědi, vyplním data, odešlu formulář, ale když si pak v debugu prohlídnu, co obsahuje $values = $form->getValues(); (v metodě, která zpracovává odeslaný formulář), tak zjistím, že obsahuje POUZE proměnné:

$values: array =
	answerCount: string = "5"
	question: string = "Nová anketa"
	answer1: string = "Odpověď 1"
	answer2: string = "Odpověď 2"

Fyzicky vidím, že vygenerovaných inputů je 5, ale data, které obsahují inputy, které jsem si vygeneroval navíc nejsou nikde.

Ola
Člen | 385
+
0
-

Smaž obsah render metody a pojede to. Testoval jsem to.

Endrju
Člen | 147
+
0
-

Ola napsal(a):

Smaž obsah render metody a pojede to. Testoval jsem to.

Jo, máš pravdu, ale potom ale vyřešit tohle? Vyplň formulář, přidej nějaké odpovědi (ale formulář neodesílej). Zobraz si třeba jiný view nebo cokoliv.. pak znova zobraz ten veiw – data, která jsi tam předím vložil tak pořád jsou (protože nebyla smazaná session), což není žádoucí.

Co chci říct – když zobrazíš view s tím formulářem, tak je potřeba, aby formulář neobsahoval hodnoty, které jsi zadával někdy předtím. A stejně tak by se měl resetovat počet výchozím odpovědí..

Session jsem tedy nemazal jen tehdy, pokud byl formulář odeslán tlačítky pro přidání/odebrání inputu. Zní to logicky ne?

Editoval Endrju (30. 6. 2010 1:10)

Ola
Člen | 385
+
0
-

Tak to smaž v nějaký úplně jiný metodě, kterou navěsíš na onclick save a cancel buttonu, případně to dej podmíněně do poolFormSubmitted.

Logicky to sice zní, ale má to jeden háček, který sem již popisoval výše – a to, že se redirectne po zmáčknutí handleAdd, čimž se celá session vždycky smaže (to další přidávání funguje jen díky tomu, že celou tu session v dalším add znovu naplníš).

Editoval Ola (30. 6. 2010 15:15)

Endrju
Člen | 147
+
0
-

Ola napsal(a):

Tak to smaž v nějaký úplně jiný metodě, kterou navěsíš na onclick save a cancel buttonu, případně to dej podmíněně do poolFormSubmitted.

Pokud bych smazání session navěsil jinam než v té metodě, která vykresluje formulář (např. na save/cancel jak píšeš), tak pokud bych neodeslal formulář, tak nedojde ke smazání session. Tedy pokud bych během manipulace s formulářem přešel na jinou stránku a pak přešel zase na stránku s formulářem, tak by tam visela pořád ta stejná data. Přemýšlel jsem nad úpravou té podmínky, za které se session maže, ale nenapadá mě, co ta dát.

Logicky to sice zní, ale má to jeden háček, který sem již popisoval výše – a to, že se redirectne po zmáčknutí handleAdd, čimž se celá session vždycky smaže (to další přidávání funguke jen díky tomu, jak že celou tu sessio v dalším add znovu naplníš).

Zmáčknutím handleAdd/handleDelete se smaže session? Takhle jsem to ale nechtěl.. přeci v té podmínce

if (!$this['poolForm']['answerAdd']->isSubmittedBy() && !$this['poolForm']['answerDel']->isSubmittedBy()) { ... }

říkám, že se má smazat JEN, když formulář odešlu něčím jiným než těmito tlačítky.. Nebo handle*** navěšený na submit tlačítko se nechová jako odeslání formu? Začínám být a little bit confused..

Ola
Člen | 385
+
0
-

Ta podmínka by byla v pohodě pokud bys nepřesměrovával – potom se už formulář nebere jako odeslaný, takže ani jeho tlačítka nejsou stisknutá.

Editoval Ola (30. 6. 2010 15:15)

Endrju
Člen | 147
+
0
-

Spadnu mi net, tak jsem nemohl rychle zareagovat a editovat post. Vyřešil jsem to. Netišil jsem, že se ta session maže vždycky – i když těmi dvěmi tlačítky formulář odesílam.. Když jsem si to uvědomil, tak jsem udělal lehkou úpravu a je to:

Jednak jsem přesunul přidávání z renderDefault do renderAdd a trochu upravil podmínku, kdy se maže session. A taky naplnění formuláře daty ze session jsem přesunul z továrny na formulář do renderAdd. Metodě renderAdd jsem přidal parametr $keepData, který určí, zda se má smazat session nebo ne. Obsah proměnné naplním při přesměrování v handle***. Celý upravený kód pak vypadá takto:

class Admin_PoolPresenter extends Admin_SecuredBasePresenter
{

	public function renderDefault()
	{

	}

	public function renderAdd($keepData = FALSE)
	{
		// jestlize nebyl formular odeslan pomoci tlacitek pro pridani/odebrani inputu
		if ($keepData != TRUE) {
			$session = Environment::getSession('poolForm');
			unset($session->poolForm); // tak vymazeme hodnoty ze session
		}
		else {
			// nastavime formulari hodnoty ze session
			$session = Environment::getSession('poolForm');
			if (isset($session->poolForm)) {
				$this['poolForm']->setDefaults($session->poolForm);
			}
		}
	}

	public function handleAddAnswer()
	{
		$this->invalidateControl('poolForm');

		$session = Environment::getSession('poolForm');
		$session->poolForm = $this['poolForm']->getValues();
		$session->poolForm['answersCount']++;

		if(!$this->isAjax()) {
			$this->redirect('this', array('keepData' => TRUE));
		}

	}

	public function handleDeleteAnswer()
	{
		$this->invalidateControl('poolForm');

		$session = Environment::getSession('poolForm');
		$session->poolForm = $this['poolForm']->getValues();
		unset($session->poolForm['answer'.$session->poolForm['answersCount']]); // zrusime data pro posledni odpoved
		$session->poolForm['answersCount']--;

		if(!$this->isAjax()) {
			$this->redirect('this', array('keepData' => TRUE));
		}
	}

	protected function createComponentPoolForm()
	{
		$form = new AppForm();

		// nacteme data formulare ze session
		$session = Environment::getSession('poolForm');

		if (isset($session->poolForm['answersCount'])) {
			$answersCount = $session->poolForm['answersCount'];
		}
		else {
			$answersCount = 2; // defaultni pocet odpovedi
		}

		$form->addHidden('answersCount')->setValue($answersCount);

		$form->addText('question', 'Otázka:')
				->addRule(Form::FILLED, 'Vložte otázku.')
				->getControlPrototype()->class('poolInput');

		$form->addSubmit('answerAdd', 'Přidat odpoveď')
			->setValidationScope(NULL)
			->onClick[] = array($this, 'handleAddAnswer');
		$form->addSubmit('answerDel', 'Ubrat odpoveď')
			->setValidationScope(NULL)
			->onClick[] = array($this, 'handleDeleteAnswer');

		if($answersCount <= 2) {
			$form['answerDel']->setDisabled(TRUE);
		}

		for ($i = 1; $i <= $answersCount; $i++) {
			$form->addText('answer'.($i), 'Odpoveď #'.($i).':')
					->addRule(Form::FILLED, 'Vložte odpověď #'.($i).'.')
					->getControlPrototype()->class('poolInput');
		}

		$form->addSubmit('save', 'Uložit');
		$form->addSubmit('cancel', 'Zrušit')->setValidationScope(NULL);

		$form->addProtection('Prosím odešlete přihlašovací údaje znova (vypršla platnost tzv. bezpečnostního tokenu).');
		$form->onSubmit[] = callback($this, 'poolFormSubmitted');

		$form->getRenderer()->setClientScript(new LiveClientScript($form));

		return $form;
	}

	public function poolFormSubmitted($form)
	{
		// Formular byl odeslan a data jsou uchovana v promenne $form.
		// Je tedy vhodne smazat data ze session, která již nebudeme potřebovat.
		$session = Environment::getSession('poolForm');
		unset($session->poolForm);


		if ($form['save']->isSubmittedBy()) {
			$id = (int) $this->getParam('id');
			$poolModel = new PoolModel();

			$values = $form->getValues();

			// ulozime do DB otazku, do promenne si ulozime jeji ID
			$questionId = $poolModel->insertQuestion(array('question' => $values['question']));

			// ulozime odpovedi
			for ($i = 1; $i <= $values['answersCount']; $i++) {
				$poolModel->insertAnswer(array('questionId' => $questionId, 'answer' => $values['answer'.$i], 'votes' => 0));
			}

			$this->flashMessage('Anketa byla vytvořena.', 'updated');
		}

		$this->redirect('default');
	}

}

Možná to není ideální řešení, ale nikdo tu s lepším nepřišel nebo jej alespoň nezveřejnil. Mě připadá fér, když už to tu řešíme funkční kód uvést.. :).

Kdyby to pak někdo dokázal i zAJAXovatět, tak by to bylo fajn. Moje pokusy o zAJAXovatění formulářů zatím neuspěly, i když jsem se inspiroval funkčními řešeními .).

Editoval Endrju (30. 6. 2010 21:18)

Endrju
Člen | 147
+
0
-

Hm, dnes jsem chtěl zprovoznit editaci, ale narazil jsem na takový oříšek:

Vytvořil jsem metodu renderEdit(), která má zobrazit komponentu s formulářem a podle počtu odpovědí (načtených z DB) vygenerovat ve formuláři patřičný počet inputů a formulář naplnit daty.

Problém spočívá v tomto: Jak nastavit proměnnou ve formuláři, podle které se vygeneruje patřičný počet odpovědí ještě před vykreslením (nebo spíše vytvořením) formuláře? Hodnotu této proměnné přitom potřebuji nastavit v metodě renderEdit(), protože na tomto místě teprve mohu zjistit, kolik odpovědí anketa má.

Zkoušel jsem to v této metodě nastavit pomocí session a ve formuláři pak tuto hodnotu ze session přečíst, ale správný počet odpovědí se vždy nastaví až při opětovném vygenerování stránky. Zdá se, že hodnota v session se nastaví až po té, co vytvoří formulář. (A nebo se v tomto mýlím…)

Uměli by jste v tomto nějak poradit?

Když to tedy shrnu, potřebuju nastavit hodnotu proměnné ještě dřív, než se vyrobí formulář a tuto proměnnou potřebuju nastavit v metodě renderEdit().

martincohen
Člen | 14
+
0
-

Skusal som to podobne ako tu popisujete, ale prislo mi to prilis kodu za vec, ktoru tak casto pouzivam (prilohy, tagy, referencie na assety, atp.) a nedaj boze ak by som chcel robit pridavanie viacerych poloziek sucasne.

Ja som to nakoniec vyriesil vlastnymi formularmi: http://coh.io/…ticcontainer

Editoval martincohen (8. 7. 2010 9:02)