Dynamicky pocet poli
- martincohen
- Člen | 14
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)
- miira
- Člen | 13
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
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:
- https://componette.org/search/?…
- https://doc.nette.org/cs/forms#…
- https://forum.nette.org/…agie-v-praxi
Snad jsi mě pochopil :)
- Vitek Jezek
- hledá kolegy | 285
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
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? :)
- Endrju
- Člen | 147
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.
- Endrju
- Člen | 147
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
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
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..
- Endrju
- Člen | 147
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
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
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)