Jeden standalone formulář pro přidávání a editaci
- Caine
- Člen | 216
Zdravím, začínám s nette a při vytváření formuláře jsem narazil na „háček“ – jak získat hodnotu hidden prvku už při vytváření formuláře resp jak byste řešili následující form vy?
//Demonstrativni kod.. :)
class MyForm extends BaseForm { //vychazi z ui/form
public function __construct($id = null) {
parent::__construct();
$this->addText('name', 'Name');
$this->addTextArea('note', 'Note');
$this->addHidden('id', $id);
if ($this['id']->getValue()) {
$this->addSubmit('save', 'Save')->onClick[] = $nejakySaveCallback;
$this->addSubmit('remove', 'Remove')->onClick[] = $prozmenuRemoveCallback;
} else {
$this->addSubmit('add', 'Add')->onClick[] = $pridavaciCallback;
}
}
}
Řešení: Presenter (container) se musí připojit už při vytváření:
use Nette\ComponentModel\IContainer
class MyForm extends BaseForm { //vychazi z ui/form
public function __construct(IContainer $container = null, $name = null, $id = null) {
parent::__construct($container, $name);
$this->addText('name', 'Name');
$this->addTextArea('note', 'Note');
$this->addHidden('id', $id);
if ($this['id']->getValue()) { //melo by byt id z POST, ale nevrati nic, tedy nikdy se tato cast vete nespusti
$this->addSubmit('save', 'Save')->onClick[] = $nejakySaveCallback;
$this->addSubmit('remove', 'Remove')->onClick[] = $prozmenuRemoveCallback;
} else {
$this->addSubmit('add', 'Add')->onClick[] = $pridavaciCallback;
}
}
}
Editoval Caine (30. 12. 2011 17:16)
- Caine
- Člen | 216
@bucrijos
První bod jsem jen zapomenul uvést, takže zdroj. kód jsem doplnil, aby to
nemátlo. ID je nutný samozřejmě získat odněkud zvenku, ale jakmile se
klikne na nějakej submit, ono ID je k dispozici už jen v hidden prvku, což
souvisí právě s bodem 2. Tak jak to tedy udělat?:)
@JuniorJR
Myslim, že to problém neřeší, protože po odeslání (submit) už ID
z konstruktoru neni k dispozici, což jsi nemoh vědet, protože jsem to tam
zapomněl dopsat.. :)
- Caine
- Člen | 216
JuniorJR Samostatnej formulář, kterej se dá použít kdekoliv (neni vázanej na jeden presenter). Např klikne se na odkaz, kde ID je známo např z url, ten přes ajax načte takovejhle formulář, kterej se po úpravách odešle, jenže tam už Id z url není k dispozici a mělo by se teda brát z onoho hidden prvku.
- Tomáš Votruba
- Moderator | 1114
Jestli to dobře chápu, potřebuješ mít pro jeden presenter dvě view
add.latte
a edit.latte
, přičemž do obou použiješ
{control myForm}
. Zatím řešíš pouze vytvoření formuláře,
takže se toho budu držet.
class MyForm extends BaseForm { // vychazí z UI/Form
public function __construct($id = null) {
parent::__construct();
$this->addText('name', 'Name');
$this->addTextArea('note', 'Note');
$id = $this->getParam("id"); // získám parametr z url, např. mysite.com/edit/27/
if($id) { // máme id -> editujeme
$this->addHidden('id', $id);
$this->addSubmit('save', 'Save')->onClick[] = $nejakySaveCallback;
$this->addSubmit('remove', 'Remove')->onClick[] = $prozmenuRemoveCallback;
}
else { // nemáme id -> přidáváme
$this->addSubmit('add', 'Add')->onClick[] = $pridavaciCallback;
}
}
}
Jestli jsem nepochopil, tak mne oprav. Určitě to nějak vyřešíme :)
Editoval Schmutzka (28. 12. 2011 2:45)
- Caine
- Člen | 216
Ve skutečnosti ten formulář chci mít nezávislej na presenteru/akcích a aby se načítal přes ajax. Tzn getParam(„id“) lze z url získat jen při prvotním načtení formuláře, ale po odeslání formuláře už Id v url nebude, tedy vždy se spustí jen větev bez Id („Add“). Jedině měnit url pro zpracování formuláře nebo Id z hidden prvku získat přes getHttpRequest()->getPost(‚id‘), ale obě řešení mi přijdou krkolomný.
- Tomáš Votruba
- Moderator | 1114
Po odeslání je id v hidden inputu. Pak tedy nejspíš invaliduješ snippet
a může tuto hodnotu nastavit zpět. Buď přes proměnnou
($this->template...
), nebo možná přímo ve formuláři
($form["id"]->setValue()
).
Zkoušels z toho něco? Co ti zatím vyhovuje nejlépe? Jak to vypadá teď? :)
- Caine
- Člen | 216
Mno můj hlavní problém je, že po odeslání formuláře se sice Id odešle, ale nedá se k němu pěkně dostat přes getValue(), protože hodnoty z post se se svejma prvkama propojej až po připojení formu k presenteru (nebo tak nějak jsem to pochopil), ale já bych k těm hodnotám chtěl přistupovat už při vytváření formuláře, takže to jedině můžu nějak obejít např. přes již zmíněný getHttpRequest()->getPost(‚id‘).
- davidm
- Člen | 81
Jestli to nebude tim ze metoda $this->getParam ve formularich neexistuje … kdyz uz to chces delat timto zpusobem tak:
class MyForm extends BaseForm { // vychazí z UI/Form
public function __construct($parent, $name)
{
parent::__construct($paretn, $name);
$this->addText('name', 'Name');
$this->addTextArea('note', 'Note');
// ve 2.0 je getParam nahrazeny za getParameter
$id = $this->presenter->getParameter("id");
if($id) { // máme id -> editujeme
$this->addHidden('id', $id);
$this->addSubmit('save', 'Save')->onClick[] = $nejakySaveCallback;
$this->addSubmit('remove', 'Remove')->onClick[] = $prozmenuRemoveCallback;
}
else { // nemáme id -> přidáváme
$this->addSubmit('add', 'Add')->onClick[] = $pridavaciCallback;
}
}
}
a musis v presenteru ten formular pripojit aby si moh pouzivat getPresenter():
createComponentMyForm($name)
{
return new MyForm($this, $name)
}
- Caine
- Člen | 216
Díky za tipy, ale pořád si nerozumíme. Ten formulář není to, co používám ve zdrojácích, ale jen demonstrativní ukázka toho, čeho bych chtěl dosáhnout, takže hledat chybky ve zdejším zápisu je nesmysl:) Snažím se tu řešit princip jak to řešit, nikoliv přímo implementaci. Když píšu, že Id není přes getParam() k dispozici (resp jen při prvotním vytváření formuláře), tak tím není myšleno to, že by pozdějš k dispozici být mělo.
Fungovat by to mělo takto. Mám obrázek, kde můžou být nějaký body. Klikáním na obrázek se přes Ajax načítá formulář. Když kliknu na místo, kde bod není, tak se načte formulář pro přidávání, když kliknu na existující bod (obsahující Id), načte se formulář editační. Problém je v tom, že u editačního fomuláře html atribut action už neobsahuje url s ID (proto chci použít hidden prvek s ID), tzn po odeslání formuláře už nejde z getParam() získat Id protože prostě není odkud, jen z hidden prvku (resp POSTu).
- davidm
- Člen | 81
Nvm jestli rozumim co presne chces, ale urcite to mas reseny pres signaly … tak treba nejak takhle
public function handleImageClicked($id = NULL)
{
if ($id) {
$this['myForm']['id']->setValue($id);
} else {
unset($this['myForm']['id'];
}
// invalidate ...
}
Editoval davidm (29. 12. 2011 19:26)
- Caine
- Člen | 216
To řeší prvotní předávání Id. Problém je, ale v tomhle:
if ($this['id']->getValue()) { //melo by byt id z POST, ale nevrati nic, tedy nikdy se tato cast vete nespusti
$this->addSubmit('save', 'Save')->onClick[] = $nejakySaveCallback;
$this->addSubmit('remove', 'Remove')->onClick[] = $
} else {
$this->addSubmit('add', 'Add')->onClick[] = $
}
getValue() vždy vrátí vždycky null. Logicky by to fungovat takhle mělo, ale někdě uvnitř si prvky svoje hodnoty získávaj až po zavolání funkce isSuccess (Nette/Forms) nebo až po připojení do presenteru (..UI/Forms). Takže asi lepší řešení než vzít hodnotu hidden prvku přímo z httpRequestu asi neni:(
- davidm
- Člen | 81
to ani nic jinyho vratit nemuze, da se to resit hodne zpusobama, napriklad muzes mit dva formulare pro Add a pro Edit pak v tom handle ten pozadovanej predas do $this->template->form a v sablone budes formular vykreslovat {$form}
nebo treba muzes pripojovat ten form pres addComponent:
public function handleImageClicked($id = NULL)
{
$this->addComponent(new MyForm($id), 'myForm');
// invalidate ...
class MyForm extends BaseForm { // vychazí z UI/Form
public function __construct($id = NULL)
{
parent::__construct();
$this->addText('name', 'Name');
$this->addTextArea('note', 'Note');
if($id) { // máme id -> editujeme
$this->addHidden('id', $id);
$this->addSubmit('save', 'Save')->onClick[] = $nejakySaveCallback;
$this->addSubmit('remove', 'Remove')->onClick[] = $prozmenuRemoveCallback;
}
else { // nemáme id -> přidáváme
$this->addSubmit('add', 'Add')->onClick[] = $pridavaciCallback;
}
}
}
- Caine
- Člen | 216
Ani takhle to řešit nepůjde (elegantně).. Při rozdílu v jen pár tlačítkách, dva formuláře zavrhuju úplně. A i kdybych použil dva formuláře, tak se problém s ID deleguje jen o úroveň výš, kde bych stejně musel použít přímo httpRequest, abych zjistil, který form vytvořit. Druhý navrhový způsob zase postrádá smysl, tedy alespoň pokud bych nepoužil postup z prvního způsobu, protože odkud se veme Id, když po odeslání je jen v hidden prvku a tím pádem se ani nikdy větev s vytvářením samotného hidden prvku nikdy po odeslání nespustí?:)
- davidm
- Člen | 81
class MyForm extends BaseForm { // vychazí z UI/Form
public function __construct($id = NULL)
{
parent::__construct();
$this->addText('name', 'Name');
$this->addTextArea('note', 'Note')
}
public function addEditFields()
{
$this->addHidden('id', $id);
$this->addSubmit('save', 'Save')->onClick[] = $nejakySaveCallback;
$this->addSubmit('remove', 'Remove')->onClick[] = $prozmenuRemoveCallback;
}
pulic function addCreateFields()
{
$this->addSubmit('add', 'Add')->onClick[] = $pridavaciCallback;
}
}
//presenter
public function createComponentAddForm()
{
$form = new MyForm();
$form->addCreateFields();
return $form;
}
public function createComponentEditForm()
{
$form = new MyForm();
$form->addEditFields();
return $form;
}
public function handleClicked($id = NULL)
{
if ($id === NULL) {
$this->template->form = $this['addForm'];
} else {
$this['editForm']['id']->setValue($id);
$this->Template->form = $this['editForm'];
}
// invalidate
}
jestli ani tohle ne tak uz me nastves :)
Editoval davidm (29. 12. 2011 22:43)
- Caine
- Člen | 216
Handle se spouští přes ?do=nazevHandlu, formulář se odesílá přes ?do=frmSubmit (nebo tak nějak).. Takže spustit se může jen jedno nebo druhý, nebo ne?;) Krom toho, formulář si do svý action url nepřebírá parametry z aktuální url, tzn když přes ajax načtu form např z /nejaka/adresa/?id=x, tak při vytvoření je parametr id k dispozici, ale action url formuláře bude jen /nejaka/adresa/?do=formSubmit (to je vlastně důvod proč potřebuju to Id předávat v hidden).
- Caine
- Člen | 216
Vlastně máš pravdu. Neuvědomil jsem si, že form se vytvoří i jen na základě ?do=nazevFormu-submit. Káždopádně ze 3 metod se stalo metod 6 a to mi už jako moc elegantní řešení nepřijde (jako drbat se levou rukou za pravym uchem:) a v podstatě je jednoduší to Id vycucat z httpRequest.
- Caine
- Člen | 216
@bene Díky díky díky, to je přesně ono!
Citováno z odkazu výše:
…takže když se předá presenter už v konstruktoru, tak při vytvoření nejakého prvku formu a přidání ho do formuláře se do něj automaticky vloží hodnota, pokud byl form odeslán…
Vyzkoušel jsem a funguje to.
Ačkoliv jsem ten topic navštívil ještě než jsem napsal tenhle (vlastně jsem projel skoro každej topic ve formulářích), ta podstatná informace mi tam nějak unikla nebo spíš jsem tomu ještě nerozumněl tolik, abych ji jako podstatnou dokázal vyhodnotit:)
Takže problém je vyřešen. Jupí:)
- Bumerank
- Člen | 30
Já řeším něco s velice podobným konceptem (1 šablona pro 2 akce – vytvoření a editace katalogu) a mám problém při editaci záznamu. Vytváření komponenty formuláře a předvyplňování polí:
<?php
protected function createComponentCatalogEditForm($name, $id = null){
$form = new Form($this, $name);
// group with basic info
$form->addGroup("Základní informace");
$form->addText("id", "ID:")->setDisabled();
$form->addText("name", "Název:");
// group with URLs TODO - add buttons to open new tab and go to URL in the field
$form->addGroup("URL Adresy");
$form->addText("url", "URL:")
->setRequired('Zadejte prosím URL!')
->addRule(Form::FILLED, "Prosím, zadejte URL katalogu.")
->addRule(Form::URL, "Neplatný tvar URL adresy.");
$form->addText("add_url", "URL začátku registrace:")
->addRule(Form::FILLED, "Prosím, zadejte URL začátku registrace.");
$form->addText("evaluation_url", "URL vyhodnocení:");
// dále vyplním hodnoty políček, pokud je předáno ID katalogu (pokud není, tak je šablona použita pro vytvoření nového záznamu)
if($this->processed_catalog != null)
{
$form["id"]->value = "111"; //$this->processed_catalog["er_catalog_id_catalog"];
$form["name"]->defaults = $this->processed_catalog["er_catalog_name"];
$form["url"]->setDefaults($this->processed_catalog["er_catalog_url"]);
$form["add_url"]->defaults = $this->processed_catalog["er_catalog_add_url"];
$form["evaluation_url"]->defaults = $this->processed_catalog["er_catalog_evaluation_url"];
}
$form->onSuccess[] = callback($this, 'doSave');
return $form;
}
?>
A problém je v tom, že se mi pak odesílají vždy původně předvyplněné hodnoty, namísto toho, co upravil uživatel. Zkoušel jsem předvyplňovat takto:
- value = „hodnota“;
- setValue(„hodnota“);
- setDefaults(„hodnota“); ..tady mi to napíše, že to nezná metodu setDefaults()
- defaults = „hodnota“; ..nezná vlastnost defaults
A když už jsem u tohoto formuláře – jde nastavit nějakými standardními vlastnostmi Nette, aby mohlo být políčko buď prázdné, nebo musí odpovídat třeba masce e-mailu, nebo si musím napsat svůj validátor? U e-mailu by mi to až tak nevadilo – horší by bylo u kontroly MIME typu (chtěl jsem použít na prvek Upload toto: ->addRule(Form::MIME_TYPE, „Soubor musí být textový (*.txt,…)!“, „text“) )
- Jan Endel
- Člen | 1016
Dle mého děláš plnění ve špatném místě, takováto operace patří do actionEdit, můžeš se podívat do kuchařky, pak to pohodlně zavoláš nějak takto (v poli $defaults jsou nabindované proměnné podle klíčů ve formuláři, protože používáš jiný klíč v poli a jiný pro název prvku, nechápu trošku proč):
public function actionEdit($id)
{
$defaults = //převedení klíču z původního pole na prvky formuláře
$form = $this->getComponent('catalogEditForm');
$form->setDefaults($defaults);
}
- Bumerank
- Člen | 30
mezi formulářovými prvky jsou i takové, jejichž hodnotu nikam neukládám a jsou tam pouze jako fíčura UI (např. radio buttonem volím jestli chci jeden z atributů zadat jako text do textarea, nebo uploadovat soubor) a trochu vidím bezpečnostní díru v tom, kdyby se inputy jmenovaly stejně jak sloupce v DB, tak jsem to raději udělal takto…nicméně toto už mi funguje :)
spíš mě těď trápí druhá část – validace…právě na té možnosti vložit text/soubor je ta zrada – buď by upload prošel jako prázdný, nebo jako textový soubor (podle MIME typu)
- Jan Endel
- Člen | 1016
Mít stejně pojmenované inputy jako db není nic špatného, když si dá člověk pozor a co se týče té validace, tak by mohla pomoci dokumentace hlavně část o validačních podmínkách.