Replicator – přidání položky ajaxem z datagridu

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

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
+
0
-

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">&times;</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
+
0
-

Nikoho nenapadá jak toho docílit? :-/

duke
Člen | 650
+
0
-

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.

jEhLa
Člen | 70
+
0
-

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?

filcau
Člen | 6
+
0
-

Tady máš řešeni

duke
Člen | 650
+
0
-

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
+
0
-

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
+
0
-

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)