RFC on component trap form, serialize also the button used to submit the form

80 views
Skip to first unread message

DenesL

unread,
Nov 7, 2014, 1:16:18 PM11/7/14
to web...@googlegroups.com
Hi all,

a bit of background on this RFC:

Any form inside a component that has ajax=True,  i.e. a form inside a LOAD(..., ajax=True) component,
will be trapped and only the fields are serialized.
According to jQuery's serialize: "No submit button value is serialized since the form was not submitted using a button."
which is not always true, quite the opposite in web2py forms, most times they will be submitted using a button.

Apart from that, I came across the need to use components with several submit buttons that should be processed differently depending on the button pressed. So I needed the serialization to include that information.
To that effect I modified the trap_form function inside web2py.js as follows:

changed

        form.submit(function (e) {
          web2py.disableElement(form.find(web2py.formInputClickSelector));
          web2py.hide_flash();
          web2py.ajax_page('post', url, form.serialize() + srlbtn, target, form);
          e.preventDefault();
        });


to
        var srlbtn;
        form.click(function (e){
          /* serialize button used to submit form (only if type is submit)*/
          var btn = e.target;
          srlbtn = btn.type=="submit" ? ('&' + (btn.name ? btn.name : btn.type) + '=' + btn.value) : "";
        });
        form.submit(function (e) {
          web2py.disableElement(form.find(web2py.formInputClickSelector));
          web2py.hide_flash();
          web2py.ajax_page('post', url, form.serialize() + srlbtn, target, form);
          e.preventDefault();
        });



Note that the button is serialized as button.name=button.value but only if button.type is 'submit'.
It seems to work with IE & Firefox.

Comments?,
Denes


Anthony

unread,
Nov 7, 2014, 2:14:32 PM11/7/14
to web...@googlegroups.com
Seems reasonable.

Anthony

Leonel Câmara

unread,
Nov 7, 2014, 2:23:02 PM11/7/14
to web...@googlegroups.com
You can just use a hidden field, like formname, or event better you can have different actions for the forms. As for the code, I don't like that this is triggered for all clicks in the form, maybe form.on('click', '[type="submit"]', ....  You also need to encodeURI the submit button's name and value.

Frankly, I don't like this much. I think this should be accomplished with the form's action.


DenesL

unread,
Nov 7, 2014, 3:11:28 PM11/7/14
to web...@googlegroups.com
Thank you for your comments Leonel.
I agree with the triggering and the more general URI encoding.
Not sure what you mean by using a hidden field, all the buttons belong to the same form, they just have different names and values.

The code has been updated as follows (relative to the original post):

        var srlbtn;
        form.on('click', 'input[type="submit"], button[type="submit"]', function (e){
          /* serialize button used to submit form */
          var btn = e.target;
          srlbtn = '&' + encodeURIComponent(btn.name ? btn.name : btn.type) + '=' + encodeURIComponent(btn.value);
        });


Denes.

Anthony

unread,
Nov 7, 2014, 3:38:24 PM11/7/14
to web...@googlegroups.com
On Friday, November 7, 2014 2:23:02 PM UTC-5, Leonel Câmara wrote:
You can just use a hidden field, like formname, or event better you can have different actions for the forms. As for the code, I don't like that this is triggered for all clicks in the form, maybe form.on('click', '[type="submit"]', ....  You also need to encodeURI the submit button's name and value.

Frankly, I don't like this much. I think this should be accomplished with the form's action.

I think the idea is to have web2py handle this automatically so the user doesn't have to manually code hidden fields for submit buttons whenever Ajax is used. Handling this in JS is probably easier than hacking the server side code to insert hidden fields.

Anthony

Niphlod

unread,
Nov 7, 2014, 4:44:51 PM11/7/14
to web...@googlegroups.com
uhm, I'm really missing the server-side code to handle such a form... could you post an example with a "real-life" implementation ?

DenesL

unread,
Nov 7, 2014, 5:30:12 PM11/7/14
to web...@googlegroups.com
Thanks Anthony, that is the idea.
Besides the multiple button scenario, and as explained in the initial post, the serialization should include the button name and value if one was used to submit the form, just like it happens with a non-ajax trapped submit.
And it seems simpler that buttons that change hidden fields, if that is what the other idea is about.

Denes

Anthony

unread,
Nov 7, 2014, 5:53:18 PM11/7/14
to web...@googlegroups.com
On Friday, November 7, 2014 4:44:51 PM UTC-5, Niphlod wrote:
uhm, I'm really missing the server-side code to handle such a form... could you post an example with a "real-life" implementation ?

It was suggested that this should be accomplished in the form's action (presumably by including some Javascript that transfers the button name to a hidden field and then reading the value of that hidden field upon submission). Assuming we want to automate that process, we'd have to add some code in gluon.html.FORM. Even in that case, we'd still need some JS, so probably best to go with a pure JS solution, such as the one Denes has proposed.

Anthony

Niphlod

unread,
Nov 7, 2014, 6:08:14 PM11/7/14
to web...@googlegroups.com
I was asking for the code handling it, but I suppose I got it .... you check for, e.g., request.post_vars.thebuttonname == thebuttonvalue

Got also why jquery "doesn't do the smart thing".
Spotted also a little error related to the disableformelement function (will send a PR to fix it) in certain conditions....

What about this ?

trap_form: function (action, target) {
     
/* traps any LOADed form */
      $
('#' + target + ' form').each(function (i) {
       
var form = $(this);
        form
.attr('data-w2p_target', target);
       
if(!form.hasClass('no_trap')) {
         
var form_data = form.serializeArray();
          form
.on('click', web2py.formInputClickSelector, function (e) {
           
var input_name = $(this).attr('name');
           
if (input_name != undefined) {
              form_data
.push({name : input_name , value : $(this).val()})
           
}
            web2py
.disableElement(form.find(web2py.formInputClickSelector));
            web2py
.hide_flash();
            web2py
.ajax_page('post', action, $.param(form_data), target, form);
            e
.preventDefault();
         
})
       
}
     
});
   
},

seems to work fine in FF, Chrome, IE9/10/11.
This seems to "fix" the misbehaviour between what the submit handler does by default and what ajax "should" do. Of course we need to bind to the click on the buttons rather than on the submission of the form...
I'm wondering if there is something "bad" to completely replace the submit event with the click event on the "submit" buttons...
If there is one (and I'd like to see it, so I'll learn yet another thing) the only method would be to create an hidden field and let the "usual" submit handler to deal with it.

Niphlod

unread,
Nov 7, 2014, 6:24:35 PM11/7/14
to web...@googlegroups.com
ah shoot, submit can also be triggered pressing "enter"...
guess we're stuck with this then...... opinions ? (if everybody agrees I'll send a PR)

trap_form: function (action, target) {
     
/* traps any LOADed form */
      $
('#' + target + ' form').each(function (i) {
       
var form = $(this);
        form
.attr('data-w2p_target', target);
       
if(!form.hasClass('no_trap')) {

          form
.submit(function (e) {
            console
.log(form.serialize())
            web2py
.disableElement(form.find(web2py.formInputClickSelector));
            web2py
.hide_flash();
            web2py
.ajax_page('post', action, form.serialize(), target, form);
            e
.preventDefault();
         
});
         
/* simulating behaviour of non-ajax forms, where the click on a submit
          button also submits its name and value */

          form
.on('click', web2py.formInputClickSelector, function (e) {

            e
.preventDefault();

           
var input_name = $(this).attr('name');
           
if (input_name != undefined) {

              $
('<input type="hidden" />').attr('name', input_name)
             
.attr('value', $(this).val()).appendTo(form)
           
}
            form
.trigger('submit');
         
})
       
}
     
});
   
},

Niphlod

unread,
Nov 7, 2014, 6:26:50 PM11/7/14
to web...@googlegroups.com
grrr... except of course the console.log line ^_^'
Time to go to sleep...

DenesL

unread,
Nov 7, 2014, 7:42:52 PM11/7/14
to web...@googlegroups.com
LOL
That seems to work too.
Would it be more efficient to add the hidden field than appending to the serialized string with the 2 calls to encodeURIComponent ?.

In any case I will get the info I need.

Thanks Niphlod.

Niphlod

unread,
Nov 8, 2014, 7:10:42 AM11/8/14
to web...@googlegroups.com
we're talking about something that goes on only on click. I feel more "safe" knowing that if some weird things are going on with either the name or the value, it'll be handled by jquery "serialize()", as any other field. Performance-wise, it's something that happens client-side, so no worries. Also, it's only binding to the "submitting" elements in the form context, which is probably the best way to do it without performance penalties from event bubbling.
@leonel, @anthony: do you spot any issues with this implementation ? 

Anthony

unread,
Nov 8, 2014, 11:32:09 AM11/8/14
to web...@googlegroups.com
In web2py.js, we have:

    formInputClickSelector: 'input[type=submit]:not([name]), input[type=image]:not([name]), button[type=submit]:not([name]), button:not([type]):not([name])'

Doesn't that only select buttons with no names? But in this case, we specifically want to capture buttons with names.

Anthony

Niphlod

unread,
Nov 8, 2014, 3:33:00 PM11/8/14
to web...@googlegroups.com
my bad. I was working on an app that had an old web2py.js. seems definitely some bad idea to exclude buttons with no name.

Niphlod

unread,
Nov 8, 2014, 5:11:53 PM11/8/14
to web...@googlegroups.com
PR in, with updates to the current situation.

DenesL

unread,
Nov 8, 2014, 8:15:19 PM11/8/14
to web...@googlegroups.com
Good catch Anthony.
My test submit button did not have a name.
Reply all
Reply to author
Forward
0 new messages