Naja + form snippet a opětovná aplikace events

Fires
Člen | 97
+
0
-

Zdravím, prosím o pomoc. Seznamuju ses Naja a Ajaxem. Trochu jsem z toho vypadnul. Mám jednoduchou stránku na které je formulář (4 inputy a submit). Ve formuláři je tabulka vložených položek. A ajaxem chci tuto tabulku položek upravovat (add, change quantity, delete).

Vše „fungovalo“ ok, ale narazil jsem na problém. Když mi ajax/naja vráti překreslenou část tabulky už na jednotlivích prvcích nemám eventy které voláji naja/ajax(logické, přidávají se .ready(). Konkrétně třeba když se mění množství položek. Trochu jsem se začetl do dokumentace naja a nette a nakonec jsem se totálně ztratil a nevím zda vůbec je něco správně :D Může mě prosím někdo poradit jaké je správně řešení?

Presenter

...
    public function handleChangeQuantity($productId, $quantity)
    {
        if ($this->isAjax()) {
            $itemsData = $this->getSession('orderList')->get('items');
            $itemsData[$productId] = $quantity;
            $this->getSession('orderList')->set('items', $itemsData);
            $this->redrawControl('itemsTable');
            $this->redrawControl('form');
        }
    }
...
<div class="container m-4">
    {snippetArea form}
    {form sellListForm}
    <div class="row">
        <div class="mb-3 col-6">
            <label for="" class="form-label">Převzato dne</label>
            <input n:name="date" type="date" class="form-control">
        </div>
        <div class="mb-3 col-6">
            <label for="" class="form-label">Email</label>
            <input n:name="email" type="email" class="form-control">
        </div>
    </div>
    <div class="row">
        <div class="mb-3 col-6">
            <label for="" class="form-label">Zákazník</label>
            <textarea n:name="customer" type="text" class="form-control" rows="4"></textarea>
        </div>

        <div class="mb-3 col-6">
            <label for="" class="form-label">Popis</label>
            <textarea n:name="note" type="text" class="form-control" rows="4"></textarea>
        </div>
    </div>
    <div n:snippet="itemsTable">
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">Položka</th>
                    <th scope="col">Název</th>
                    <th scope="col">Množství</th>
                    <th scope="col">Jednotka</th>
                    <th scope="col">Cena ks</th>
                    <th scope="col">Cena celkem</th>
                    <th scope="col">Akce</th>
                </tr>
            </thead>
                <tbody>
                    {var $sum=0}
                    {foreach $items as $key=>$value}
                    <tr>
                        <th scope="row">{$iterator}</th>
                        <td>{$itemsForSale[$key]->POPIS}</td>
                        <td><input data-id="{$key}" data-url={link ChangeQuantity!} type="number" class="ajaxInputEdit"
                                value="{$value}"></td>
                        <td>{$itemsForSale[$key]->MJ}</td>
                        <td>{$itemsForSale[$key]->CENA_PRODEJ} Kč</td>
                        {var $sum+=$itemsForSale[$key]->CENA_PRODEJ*$value}
                        <td>{$itemsForSale[$key]->CENA_PRODEJ*$value} Kč</td>
                        <td><a n:href="removeItem! $itemsForSale[$key]->ID"><button n:name="generate" type="button"
                                    class="btn btn-danger">Smazat</button></a></td>
                    </tr>
                    {/foreach}
                </tbody>
                <tfoot>
                    <tr>
                        <td></td>
                        <td></td>
                        <td></td>
                        <td></td>
                        <td><b>Celkem</b></td>
                        <td>{$sum} Kč</td>
                        <td></td>
                    </tr>
                    <tr>
                        <td></td>
                        <td></td>
                        <td></td>
                        <td></td>
                        <td><b>Celkem s DPH</b></td>
                        <td>{$sum*1.21|round} Kč</td>
                        <td></td>
                    </tr>
                </tfoot>
        </table>

    </div>
    <input type="hidden" n:name="signature" id="signatureInput">
    <div class="row">
        <div class="col-6">
            <button n:name="generate" type="submit" class="btn btn-primary">Vygenerovat</button>
        </div>
        <div id="signatureparent" class="col-6">
            <h3>Podpis</h3>
            <canvas id="signaturecanvas" width="100%" height="100" style="border:2px solid; width: auto;"></canvas>
        </div>
    </div>
    {/form}
    {/snippetArea}
$(document).ready(function () {
    naja.initialize();


    //.ajaxInputEdit
    $(".ajaxInputEdit").change(function (e) {
        naja.makeRequest('GET',$(this).attr("data-url") , {
            productId: $(this).attr("data-id"),
            quantity: $(this).val()
        });

    }
    );



    $("#frm-sellListForm").submit(function (event) {
        console.log();
        $("#signatureInput").val(document.getElementById('signaturecanvas').toDataURL());
    });

});

Předem díky.

m.brecher
Generous Backer | 870
+
0
-

@Fires

Když mi ajax/naja vráti překreslenou část tabulky už na jednotlivích prvcích nemám eventy které voláji naja/ajax

Ahoj,

jquery neznám, ale ono se to dá odhadnout, protože moderní javascript to dost okopíroval. Předpokládám, že ty eventy, které Ti mizí po překreslení jsou toto:

$(".ajaxInputEdit").change(function (e) {
    naja.makeRequest('GET',$(this).attr("data-url") , {
        productId: $(this).attr("data-id"),
        quantity: $(this).val()
    });

Myslím, že po překreslení je potřeba, aby Naja přes nějaký vhodný event obnovila ty eventy, co překreslením zmizely. Ty se totiž nastavily v události DOMContentLoaded documentu, a tato událost se volá jenom při prvním neajaxovém natažení stránky (
javascriptová obdoba toho $(document).ready()).

$(document).ready(function () {...}

Takže musíš ty samé eventy vyvolat v eventu Naja.?, který reprezentuje překreslení ajaxového snippetu.

Z hlavy teď nevím, jaký event to je, ale bude to v dokumentaci Naja.

Editoval m.brecher (11. 3. 2023 0:44)

m.brecher
Generous Backer | 870
+
0
-

@Fires

Ještě posílám nástřel řešení v čistém javascriptu, protože kód jquery mě přijde zbytečně komplikovaný. Psal jsem to z hlavy, takže je potřeba to otestovat a doladit. Takhle nějak bych doporučil to napsat.

<script>
	function addInputEvents(inputClass)
	{
		document.querySelectorAll(inputClass).forEach(input => {
			input.addEventListener('change', () => {
				naja.makeRequest('GET', input.dataset.url, {
					productId: input.dataset.url,
					quantity: input.value
				})
			})
		})
	}

	document.addEventListener('DOMContentLoaded', () => {
		naja.initialize();

		/* input eventy po loadu stránky a po překreslení snippetů */
		let inputClass = '.ajaxInputEdit'
		addInputEvents(inputClass)
		naja.addEventListener('?', () => addInputEvents(inputClass))  // tady doplň ten správný event naja

		/* submit formuláře */
		document.querySelector('#frm-sellListForm')
				.addEventListener('submit', () =>
					document.querySelector('#signatureInput').value = document.getElementById('signaturecanvas').toDataURL()
				)

	})
</script>

Editoval m.brecher (11. 3. 2023 2:16)

m.brecher
Generous Backer | 870
+
0
-

@Fires

Ta událost v Naja ve které je potřeba znovu nastavit input eventy je complete:

https://naja.js.org/#…

Fires
Člen | 97
+
0
-

m.brecher napsal(a):

@Fires

Ta událost v Naja ve které je potřeba znovu nastavit input eventy je complete:

https://naja.js.org/#…

Super diky. Jinak přístup ze strany nette je takto v pořádku? Obalit celý form do snippetarea, poté tu část kterou regenerovat jako snippet a invalidovat to pak cele?

m.brecher
Generous Backer | 870
+
0
-

@Fires

Obalit celý form do snippetarea, poté tu část kterou regenerovat jako snippet a invalidovat to pak cele?

Hmm… spíš ne, ty by Jsi sice mohl překreslovat vždy jeden řádek tabulky + patičku tabulky pomocí dynamických snippetů, ale udělal Jsi to tak, že překreslíš celou tabulku – což úplně stačí a je to jednoduché.

SnippetArea se dle dokumentace použije pro označení inkludované šablony, ve které je překreslovaný snippet. O tom, že by {form} se měl obalovat jsem v dokumentaci nic nenašel, tak by měl stačit jenom ten snippet.

Ale když překresluješ v podstatě celý formulář, nebylo by jednodušší prostě překreslit rovnou celou komponentu (formulář), když už se stejně vykresluje skoro celá šablona? Kód šablony se stejně vykoná celý, jenom se pošle o pár bytů html více. https://doc.nette.org/…ication/ajax#…

To by Jsi ale musel mít formulář zabalený do vykreslovací komponenty odvozené z třídy Control, aby měla komponenta vlastní šablonu. Což tady asi není. Takže to nechej jak je a zkus snippetArea odstranit.

Editoval m.brecher (11. 3. 2023 21:44)

Fires
Člen | 97
+
0
-

m.brecher napsal(a):

@Fires

Obalit celý form do snippetarea, poté tu část kterou regenerovat jako snippet a invalidovat to pak cele?

Hmm… spíš ne, ty by Jsi sice mohl překreslovat vždy jeden řádek tabulky + patičku tabulky pomocí dynamických snippetů, ale udělal Jsi to tak, že překreslíš celou tabulku – což úplně stačí a je to jednoduché.

SnippetArea se dle dokumentace použije pro označení inkludované šablony, ve které je překreslovaný snippet. O tom, že by {form} se měl obalovat jsem v dokumentaci nic nenašel, tak by měl stačit jenom ten snippet.

Ale když překresluješ v podstatě celý formulář, nebylo by jednodušší prostě překreslit rovnou celou komponentu (formulář), když už se stejně vykresluje skoro celá šablona ?? Kód šablony se stejně vykoná celý, jenom se pošle o pár bytů html více. https://doc.nette.org/…ication/ajax#…

To by Jsi ale musel mít formulář zabalený do vykreslovací komponenty odvozené z třídy Control, aby měla komponenta vlastní šablonu. Což tady asi není. Takže to nechej jak je a zkus snippetArea odstranit.

Mno muj problém je takovej že jsem se zaleknul dynamického formuláře a ty jednotlivé řádky nejsou součástí formuláře. Případně nevím jak řešit „košík“ ve formuláři. Proto to volání externě. Ajax upravi pouze pole v session a při odeslání formuláře se vemou hlavičková data (jméno, etc.) a to co je v session a poskládá se z toho „objednávka“.

m.brecher
Generous Backer | 870
+
+1
-

@Fires

Mno muj problém je takovej že jsem se zaleknul dynamického formuláře

OK, jde to překreslovat celé a funguje to taky, rozdíl bude jen pár stovek bytů html

a ty jednotlivé řádky nejsou součástí formuláře

potom máš od sebe oddělená data v session a vykreslená v tabulce, při submitu formuláře data co jsou v tabulce vidět neposíláš, ale uložíš to, co máš v session. Ajax sice data ze session překresluje do tabulky, ale stačí nějaká drobná chyba někde v javascriptu a může se ti stát, že v session nebude úplně přesně to, co bude vidět v tabulce. Navíc nevyužíváš zabezpečení a sanitizace dat která je ve formulářích vestavěná.

Lepší je mít všechny data ve formulářových prvcích a při submitu formuláře je uložit standardním způsobem a data v session mít pouze jako takovou dočasnou zálohu.

Fires
Člen | 97
+
+1
-

m.brecher napsal(a):

@Fires

Mno muj problém je takovej že jsem se zaleknul dynamického formuláře

OK, jde to překreslovat celé a funguje to taky, rozdíl bude jen pár stovek bytů html

a ty jednotlivé řádky nejsou součástí formuláře

potom máš od sebe oddělená data v session a vykreslená v tabulce, při submitu formuláře data co jsou v tabulce vidět neposíláš, ale uložíš to, co máš v session. Ajax sice data ze session překresluje do tabulky, ale stačí nějaká drobná chyba někde v javascriptu a může se ti stát, že v session nebude úplně přesně to, co bude vidět v tabulce. Navíc nevyužíváš zabezpečení a sanitizace dat která je ve formulářích vestavěná.

Lepší je mít všechny data ve formulářových prvcích a při submitu formuláře je uložit standardním způsobem a data v session mít pouze jako takovou dočasnou zálohu.

Mnohokrát děkuji za vyčerpávající odpovědi. Vrátím se zpět k samotnému návrhu a zkusím to předělat celé do formu. Není asi problém si do formu přidat dynamicky inputy do zvláštního containeru, podle toho co bude v session jako uložený košík.

m.brecher
Generous Backer | 870
+
0
-

@Fires

Není asi problém si do formu přidat dynamicky inputy do zvláštního containeru, podle toho co bude v session jako uložený košík.

Ano, košík se ukládá do session při zobrazení košíku se formulář vygeneruje z dat v session košíku – tedy jednotlivé produkty, cena/produkt, počty kusů, sazba DPH atd… a form se vykreslí do šablony.

Je mnoho cest jak to udělat.

Třeba takhle. Nemusíš vůbec řešit eventy na jednotlivých inputech, ale centrálně si zachytit událost onchange rovnou na formuláři jako takovém, protože eventy v javascriptu v DOM bublají, tj. událost change na inputu bublá nahoru strukturou formuláře a tatáž událost jde odchytit a zpracovat v hierarchii výše tj. i na samotném <form>. A není nutné zpracovávat ajax či snippety jednotlivých inputů, ale jednoduše při change na formuláři odpálit ajaxový submit formuláře a nechat překreslit celou šablonu formuláře. Musíš ale odlišit ajaxový submit formu, který se vyvolá událostí change a uživatelský submit formu, kdy se kliká na tlačítko, abys na serveru věděl jak to zpracovat.