Replicator – přidání položky ajaxem z datagridu
- jEhLa
- Člen | 70
Dobrý den,
nevím si vůbec rady s vytvořením formuláře, který bude obsahovat
replicator do kterého budou položky přidávány ajaxem z datagridu.
Mám formulář, kde je odkaz „Přidat položku“, to otevře v modalu komponentu Nextras\Datagrid, kde si vyhledám požadovanou položku. A po jejím vybrání bych ji potřeboval přidat do replicatoru původního formuláře.
Chtěl jsem to vyřešit tak, že si do datagridu přidám na řádek odkaz
„vybrat“ který zavolá signál na presenteru a předá mu id vybrané
položky. Při zpracování signálu si načtu požadovanou položku a
invaliduji část šablony s replicatorem.
To sice funguje pro přidání jedné položky, jakmile však chci přidat
druhou, tak se první položka ztratí a nahradí tou nově přidanou.
Vím že je to tím, že při přidání položky odesílám pomocí GET
pouze informaci o přidání nové položky, ale už né o těch, které jsou
již přidaný. Nevím si ale vůbec rady jak docílit toho že se mi přidá
nově zvolená položka a zachovají se i ty dříve přidané. (Prostě nějak
submitnout ten původní formulář a zavolat na něm metodu replicatoru pro
přidání nové položky a rovnou ji nastavit hodnoty podle vybraného id.)
Snad jsem to napsal srozumitelně :-/
Mohl by mě prosím někdo aspoň nasměrovat?
Děkuji
- jEhLa
- Člen | 70
Nikoho nic nenapadá? Nebo jsem to blbě popsal?
Co mě napadlo… ale nejsem schopný to rozchodit:
Presenter:
class ReceiptPresenter extends \App\BackModule\Presenters\BasePresenter
{
public function renderDefault()
{
$this->template->form = $this->template->_form = $this["receiptForm"];
}
protected function createComponentReceiptForm()
{
\Kdyby\Replicator\Container::register();
$form = new \App\Components\Form;
$now = new \Nette\Utils\DateTime('now');
$form->addText('datetime','Datum:')
->setDefaultValue( $now->format( 'Y-m-d\TH:i' ))
->setType('datetime-local');
$replicator = $form->addDynamic('products', function (\Nette\Forms\Container $container) {
$container->addHidden('id');
$container->addText('quantity', 'Kusů:',2)->setDefaultValue(1)->setType('number');
$container->addText('price', 'Cena:',8);
$container->addSubmit('remove', 'Odebrat')
->addRemoveOnClick()
->getControlPrototype()->data['confirm'] = "Opravdu odebrat?";
}, 0);
$form->addSubmit('submit', 'Uložit');
$form->onSuccess[] = $this->receiptFormSuccess;
return $form;
}
public function receiptFormSuccess( \Nette\Forms\Form $form )
{
}
public function handleShowProductModal()
{
$this->template->showProductModal = true;
$this->redrawControl('productModal');
}
//komponenta obsahující komponentu kategorií a datagrid
protected function createComponentProductsList()
{
$control = new \App\StockModule\Components\ProductsList($this->productsModel,$this->categoriesModel);
$control['grid']->onSelect[] = $this->selectProduct;
return $control;
}
public function selectProduct( $product_id )
{
$product = $this->productsModel->getById( $product_id );
$this['receiptForm']['products']->createOne($product_id)->setValues( $product->toArray() );
//příprava pro ajax
//$this->redrawControl('products');
//$this->redrawControl('productModal');
}
}
Šablona:
{form receiptForm}
<h3>Obecné</h3>
<div class="controls">
<p>
<span>{label datetime/}</span>{input datetime}
</p>
</div>
<h3>Zboží</h3>
<a n:href="showProductModal!" class="btn add ajax">Přidat položku</a>
{formContainer products}
<ul class="list" n:snippet="products">
<li n:foreach="$form['products']->containers as $product">
{*name*}
{input $product['id']}
{label $product['quantity']/}{input $product['quantity']}
{label $product['price']/}{input $product['price']}
{input $product['remove']}
</li>
</ul>
{/formContainer}
{input submit}
<div n:snippet="productModal" n:inner-if="isset($showProductModal)" class="modal fade">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title">Přidání položky</h4>
</div>
<div class="modal-body">
{control productsList}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Přidat</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{/form}
Control productList obsahuje komponentu ProductsGridSelectable dědící od ProductsGrid dědící od \Nextras\Datagrid.
Komponenta ProductsGridSelectable:
class ProductsGridSelectable extends ProductsGrid
{
public $onSelect;
protected function createComponentSelectButton()
{
return new \Nette\Application\UI\Multiplier( function ($product_id) {
$form = new \App\Components\Form;
$form->addHidden('id')->setDefaultValue($product_id);
$form->addSubmit('add', 'Přidat');
$form->onSuccess[] = $this->selectSuccess;
return $form;
} );
}
public function selectSuccess( \Nette\Forms\Form $form )
{
$this->onSelect( $form->getValues()->id );
}
}
Šablona gridu pro akci řádku:
{define row-actions}
{control selectButton-$row->id}
{/define}
A teď si nevím rady. Po kliknutí na SelectButton se mi položka do
replicatoru přidá, ale to je vše, žádná data původního stavu formuláře
se neodesílají.
POST obsahuje pouze:
array (3)
add => "Přidat" (7)
id => "2"
do => "productsList-grid-selectButton-2-submit" (39)
Jak tam dostat i ty inputy hlavního formuláře?
- jEhLa
- Člen | 70
duke napsal(a):
Chceš-li to řešit odesíláním všech formulářových dat, musíš si uvědomit, že aby se odesílalo vše, musíš vše také umístit do jednoho formuláře. Takže instanciaci nového Formu v Multiplieru bys měl nahradil manipulací s hlavním formulářem.
No to si právě uvědomuju, ale vůbec nevím jak sním mám manipulovat?
- duke
- Člen | 650
Proč to vlastně řešíš přes zvláštní komponentu (a multiplier)?
Můžeš ta submitovací tlačítka přeci generovat už při vytváření
formuláře. Pojmenovat je můžeš tak, aby s sebou nesla id produktu,
případně použít kontejner. S kontejnerem by to mohlo vypadat
následovně:
public function createComponentForm()
{
$form = parent::createComponentForm();
$productIds = ... // TODO
$add = $form->addContainer('add');
foreach ($productIds as $productId) {
$addButton = $add->addSubmit($productId, 'Přidat');
$addButton->onClick[] = $this->selectProduct;
}
}
A v metodě selectProduct si pak podle názvu submitovacího tlačítka zjistíš product id:
public function selectProduct(SubmitButton $button)
{
$productId = (int) $button->name;
...
}
Editoval duke (9. 12. 2014 13:55)
- jEhLa
- Člen | 70
No ono to je hlavně asi celý špatně, mám tam zanořený form.
{form receiptForm}
.
.
.
{formContainer products}
.
.
{/formContainer}
{input submit}
//modal
{control productsList}
{/form}
Jenže ten control productsList, obsahuje datagrid a ten obsahuje form pro
vyhledávání. Myslel jsem si že nette nějak zvládne vnořený form
převést na container a nějak sním pracovat. Ale byla to jen chyba že jsem
koukal na strukturovaný html výstup v Chromu, ale ten si ten kód udělal
validní. Ale v čistým html došlo k tomu že bylo
<form>…<form>…<form>…</form></form></form>.
Takže toto je celé špatně asi.
Budu to muset udělat tak že ten datagrid dám mimo {form receiptForm} a
pomocí JS si budu odchytávat kliknutí na to tlačítko a následně posílat
vlastní ajax požadavek.
- filcau
- Člen | 6
duke napsal(a):
Proč to vlastně řešíš přes zvláštní komponentu (a multiplier)?
Můžeš ta submitovací tlačítka přeci generovat už při vytváření formuláře. Pojmenovat je můžeš tak, aby s sebou nesla id produktu, případně použít kontejner. S kontejnerem by to mohlo vypadat následovně:public function createComponentForm() { $form = parent::createComponentForm(); $productIds = ... // TODO $add = $form->addContainer('add'); foreach ($productIds as $productId) { $addButton = $add->addSubmit($productId, 'Přidat'); $addButton->onClick[] = $this->selectProduct; } }
A v metodě selectProduct si pak podle názvu submitovacího tlačítka zjistíš product id:
public function selectProduct(SubmitButton $button) { $productId = (int) $button->name; ... }
nom problém byl v tom že jsem si neuvědomil že přece name mužu použit jako id a snažil jsem se narvat id do value u imutu to ted docela zkrati kod když na to koukam :-)
no ve vlastni komponentě to mam kvuli tomu aby se to dalo zase použit jinde přece ;-)
Editoval filcau (10. 12. 2014 3:08)