Re-submitting multiplied Form rendered in <tr> redrawed by snippet ajax

jirkov
Member | 3
+
0
-

Hi. I am using Naja.js to handle ajax. Here are 3 same forms multiplied by Multiplier rendered in table>tr in 3 different ways. Form is submitted by Naja ajax and redrawed correctly after first submission. But only first 2 ‘mysnippet’ and ‘mysnippet2’ can be submitted again after snippets redraw.

{snippet mysnippet}
    <table>
        <tr n:foreach="$items as $item">
            <td>
                {control form2-$item}
            </td>
        </tr>
    </table>
{/snippet}

{snippet mysnippet2}
    <table>
        <tr n:foreach="$items as $item">
            <td>
                {form form2-$item}
                    {input submit}
                {/form}
            </td>
        </tr>
    </table>
{/snippet}

{snippet mysnippet3}
    <table class="formTable">
        <tbody>
            <tr n:foreach="$items as $item">
                {form form2-$item}
                    <td>
                        <input n:name=submit>
                    </td>
                {/form}
            </tr>
        </tbody>
    </table>
{/snippet}

What is different in these 3 ways of rendring is that in mysnippet3 the inputs are rendered outside <form></form> tag but this is OK in terms of valid HTML. I am not sure whether it is Naja or Form/Latte issue or I do miss something.

How do you solve forms on table>rows repeatedly submitted by ajax? Thank you in advance.
I searched for the solution few times, this is not firs time I try to solve this

m.brecher
Generous Backer | 758
+
0
-

Hi @jirkov

But only first 2 ‘mysnippet’ and ‘mysnippet2’ can be submitted again after snippets redraw.

This means that forms in the first group after first submit redraw correctly but next submitting works not ?? Here we need to inspect whats wrong – look the rendered html of the forms, the problems might be in slight different html after redraw. You unfortunately didnt posted other important code – the forms and presenter. May be something is wrong there ??

If you compare working and not working variants – works not variant with {control}, where form is rendered as component. Do you use for this form a latte template ? And what code is insight ?

jirkov
Member | 3
+
0
-

Hi @m.brecher

Just for simplification I removed mysnippet as the form is rendered by {control …} in there and it simply works.

In mysnippet2 and mysnippet3 I use {form …} to render the form. Problem is that the form in mysnippet3 is not submited after first submission (after ajax snippets redraw).

I have checked HTML code in <div id=“snippet–mysnippet3”> after snippet redraw and it is absolutely the same as the original one after first page full load (F5). Only time in submit values changes – this I use to recognize form redraw.

In presenter there is:

    public function startup(): void
    {
        parent::startup();
        $this->items = [1,2];
    }

    public function renderDefault(): void
    {
        $this->template->items = $this->items;
    }

    public function createComponentForm2(): Multiplier
    {
        return new Multiplier(function($id) {
            $form = $this->formFactory->create(ajax:true);

            $time = DateTime::from("now")->format("H:i:s");
            $form->addSubmit('submit', 'ok '.$time);
            $form->onSuccess[] = [$this, 'Form2Succeeded'];
            return $form;
        });
    }

    public function Form2Succeeded($form, $vals): void
    {
        if($this->isAjax()){
            $this->redrawControl();
        }else{
            $this->redirect('this');
        }
    }

Resulting HTML is here.

<div>
	<div id="snippet--mysnippet2">
		<table>
        	<tbody>
				<tr>
                	<td>
                    	<form action="/vypis/fix/" method="post" class=" ajax" id="frm-form2-1">
                        	<input type="submit" name="_submit" value="ok 11:02:19">
		                    <input type="hidden" name="_do" value="form2-1-submit">
							<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
						</form>
	                </td>
	            </tr>
	            <tr>
    	            <td>
        	            <form action="/vypis/fix/" method="post" class=" ajax" id="frm-form2-2">
            	            <input type="submit" name="_submit" value="ok 11:02:19">
                		    <input type="hidden" name="_do" value="form2-2-submit">
							<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
						</form>
	                </td>
    	        </tr>
	        </tbody>
		</table>
	</div>

    <hr>

	<div id="snippet--mysnippet3">
		<table>
        	<tbody>
                <tr>
                    <form action="/vypis/fix/" method="post" class=" ajax" id="frm-form2-1"></form>
                    <td>
                    	<input type="submit" name="_submit" value="ok 11:02:19">
                    </td>
	                <input type="hidden" name="_do" value="form2-1-submit">
					<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
	            </tr>
                <tr>
                    <form action="/vypis/fix/" method="post" class=" ajax" id="frm-form2-2"></form>
                    <td>
                    	<input type="submit" name="_submit" value="ok 11:02:19">
                    </td>
                    <input type="hidden" name="_do" value="form2-2-submit">
					<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
                </tr>
            </tbody>
        </table>
	</div>
</div>
nightfish
Member | 472
+
+1
-

@jirkov
Your HTML in mysnippet3 is not valid. As per specification <tr> cannot contain <form> as its immediate child.

It can be seen on the resulting HTML you posted:

jirkov wrote:
Resulting HTML is here.

	<div id="snippet--mysnippet3">
		<table>
        	<tbody>
                <tr>
                    <form action="/vypis/fix/" method="post" class=" ajax" id="frm-form2-1"></form>
                    <td>
                    	<input type="submit" name="_submit" value="ok 11:02:19">
                    </td>
	                <input type="hidden" name="_do" value="form2-1-submit">
					<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->
	            </tr>

You see that <form> is immediatelly closed by </form> and your submit button is therefore outside of the form. That would probably be enough to prevent form from submitting.

Fix your HTML structure and the problem will resolve itself.

m.brecher
Generous Backer | 758
+
0
-

@jirkov

I have checked HTML code in <div id=“snippet–mysnippet3”> after snippet redraw and it is absolutely the same as the original one after first page full load (F5).

As @nightfish commented above – html code in mysnippet3 is invalid – fix this will solve all problems. What is however interesting that html is identical before and after first submit and first submit works, second not.

It depends what html structure you inspect – static in source code of the page or real one in browser DOM. In case that html is invalid the browser might create element nodes in browser DOM in a different way than you expect. Inspect the html code of 3. form in DOM with Chrome DevTools and inspect the real structure of the 3. form before and after submitting.

jirkov
Member | 3
+
0
-

Finally I have a working solution (googled for “form in tr”). Adding form="" attribute with form id as their value to all form inputs match those inputs to the form. Naja.js then submits form correctly after snippets redraw with inputs still outside form tag.

{snippet mysnippet3}
    <table>
        <tbody>
            <tr n:foreach="$items as $item">
                {form form2-$item}
                    <td>
                        <input n:name=submit form="frm-form2-{$item}">
                        <input n:name=_do form="frm-form2-{$item}">
                    </td>
                {/form}
            </tr>
        </tbody>
    </table>
{/snippet}

It is necessary to add form="" attribute also to hidden input with name=“_do”
Does anybody know how to do that in php when creating that form?

Thank you guys @nightfish @m.brecher you were right – form inside or outside tr is not valid. That is why browser modifies DOM so that <form></form> is before the first td and inputs are outside form. But this happens in both cases first full page load and snippet redraw. I have checked final HTML in browser dev tools and HTMLs were the same. Still submission after ajax snippet redraw did not work.

I knew (just feeling) that tr>form>td>input is not super correct but Latte allowes it and those forms worked correctly and I used it very often. But when I started “ajaxify” large tables with meny rows with form on each I struggled with 2nd ajax submit all the time. That was why I originally asked How do you solve forms on table>rows repeatedly submitted by ajax?

To me form=“{$formid}” on all the inputs is the solution.

David Grudl
Nette Core | 8139
+
+3
-

I've considered in the past that if <form> has an id attribute, it could automatically add a form attribute with that value to all elements. That would solve the _do issue as well.

mskocik
Member | 53
+
0
-

David Grudl wrote:

I've considered in the past that if <form> has an id attribute, it could automatically add a form attribute with that value to all elements. That would solve the _do issue as well.

This would be really nice, maybe just enable-able feature. Because form has id attribute always set if I am not mistaken.