How to submit Ext forms the right way


Updated 2008/03/12: The code got updated, read more at the bottom.

When you work with Ext, sooner or later you will stumble across a design desicion made by the ext developers: Submitting forms is done in the traditional way. This means that the values submitted come from the value field of the input-tag inside the form. This leads to several problems:

  • Checkboxes which are not checked are not submitted at all (as normal html-forms also do)
  • Comboboxes get their displayed value submitted, and not the value behind the combobox
  • DateFields get their displayed date submitted, which may be different based on the users locale
  • All Formitems which return something different in their getValue() method from what is written inside the input tags value attribute will probably not be submitted correctly.

Thankfully, after some pondering, I developed a nice solution: OOSubmit!

OOSubmit is a custom Ext.form.Action which submits forms in an object-oriented way. - Hence the name OOSubmit. Instead of going trough the forms input-elements and getting their value attributes, this submit action goes through the form items and calls their corresponding getValue() method. It also looks if the item has a getSubmitValue() method and used that instead of getValue() if present.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Ext.namespace("Ext.ux");

/**
 * This submit action is basically the same as the normal submit action,
 * only that it uses the fields getSubmitValue() to compose the values to submit,
 * instead of looping over the input-tags in the form-tag of the form.
 *
 * To use it, just use the OOSubmit-plugin on either a FormPanel or a BasicForm,
 * or explicitly call form.doAction('oosubmit');
 *
 * @param {Object} form
 * @param {Object} options
 */

Ext.ux.OOSubmitAction = function(form, options){
    Ext.ux.OOSubmitAction.superclass.constructor.call(this, form, options);
};

Ext.extend(Ext.ux.OOSubmitAction, Ext.form.Action.Submit, {
    /**
    * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
    * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
    * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
    * is performed.
    */

    type : 'oosubmit',

    // private
    /**
     * This is nearly a copy of the original submit action run method
     */

    run : function(){
        var o = this.options;
        var method = this.getMethod();
        var isPost = method == 'POST';

        var params = this.options.params || {};
        if (isPost) Ext.applyIf(params, this.form.baseParams);

        //now add the form parameters
        this.form.items.each(function(field)
        {
            if (!field.disabled)
            {
                //check if the form item provides a specialized getSubmitValue() and use that if available
                if (typeof field.getSubmitValue == "function")
                    params[field.getName()] = field.getSubmitValue();
                else
                    params[field.getName()] = field.getValue();
            }
        });

        //convert params to get style if we are not post
        if (!isPost) params=Ext.urlEncode(params);

        if(o.clientValidation === false || this.form.isValid()){
            Ext.Ajax.request(Ext.apply(this.createCallback(o), {
                url:this.getUrl(!isPost),
                method: method,
                params:params, //add our values
                isUpload: this.form.fileUpload
            }));

        }else if (o.clientValidation !== false){ // client validation failed
            this.failureType = Ext.form.Action.CLIENT_INVALID;
            this.form.afterAction(this, false);
        }
    },

});
//add our action to the registry of known actions
Ext.form.Action.ACTION_TYPES['oosubmit'] = Ext.ux.OOSubmitAction;

To use this, simply include the code somewhere in your page, and when you want to submit the form dont use form.submit() but form.doAction(’oosubmit’); This way you may always submit the form in the traditional way.

To make it even easier, i wrote a small plugin which can be plugged into a BasicForm or FormPanel. It overrides the standard submit() action and uses the OOSubmit action by default.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * This plugin can be either used on BasicForm or FormPanel.
 * In both cases it changes the behaviour of submit() to use
 * the 'oosubmit' action instead of the 'submit' action.
 */

Ext.ux.OOSubmit=function()
{

    this.init=function(_object)
    {
        var form=null;
        if (typeof _object.form=="object")
        { //we are a formpanel:
            form=_object.form;
        }
        else form=_object;

        //Save the old submit method:
        form.oldSubmit=form.submit;

        //create a new submit method which calls the oosubmit action per default:
        form.submit=function(options)
        {
              this.doAction('oosubmit', options);
              return this;
        };
    };

};

To use the plugin, simply constuct a BasicForm or FormPanel and add this to the config:

plugins: [new Ext.ux.OOSubmit()]

- Done.

The checkbox problem and the combobox gets solved by OOSubmit automatically, because the getValue() function is used.

To fix the datetime problem mentioned above I spent Ext.form.DateField a getSubmitValue() function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * Returns the submit value of the datefield, always in the same format,
 * regardless of display format.
 * The format returned is Y-m-d, because this is common format used in rdbms. (mysql for example)
 *
 */

Ext.form.DateField.prototype.getSubmitValue=function()
{
    var v = this.getValue();
    if(v !=='')
    {
       var date= new Date(v);
       return date.format("Y-m-d");
    }
    return v;
};

Thats it! Now everything works like it should (IMHO).

Questions, ideas for improvements, bugs?

Update on 2008/03/12:

OOSubmit now also supports baseParams of forms in POST and GET mode, also custom params passed to form.doAction() are now honored.
As mentioned in the ext forums, the code will now discard disabled fields and not submit them as normal.
The bugs mentioned in the comments have also been fixed. The code should now work out of the box.

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts
Improved code display with wp-syntax
MySQL Workbench + propel = useful

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

there’s a bug in the run method:

typeof file.getSubmitValue

should be

typeof field.getSubmitValue

Thanks for the feedback. It is now fixed. - That happens when you edit code without running it. :-)

I love the code! It addresses all the issues I’ve run into with Ext form submission. I’m going to implement it in my framework.

It doesn’t work. I’ve incldued only the code related to the extended class (Ext.ux.OOSubmitAction…).
Then I submit the form with form.doAction(’oosubmit’) and all I got is an error in firebug:

Ext.form.Action.ACTION_TYPES[action] is not a constructor.

It’s seems that something is missing for using it not as plugin.

2 fixes to make it really working.

1) It seems that you have to register the type by adding this after class extension:
Ext.form.Action.ACTION_TYPES['oosubmit'] = Ext.ux.OOSubmitAction;

2) This code will work better for the date issue:
Ext.form.DateField.prototype.getSubmitValue=function()
{
var v = this.getValue();
if(v !== ”){
var date= new Date(v);
return date.format(”Y-m-d”);
}
return v;
};

In your code is missing a Date object and you haven’t returned a proper value when no data has been selected.

Missing registration of the action?

Ext.form.Action.ACTION_TYPES['oosubmit'] = Ext.ux.OOSubmitAction;

Another error

plugin: new Ext.ux.OOSubmit()

it should be

plugins: new Ext.ux.OOSubmit()

Another problem with this solution is that if fileUpload is set to true file aren’t uploaded

First: I apologize for not responding quickly. I’m currently moving at home and have not been able to get online for some days.
Thanks very much for your reponsens!
@kimu: You are right, i simply forgot that line when copying it out from my file, sorry! Also thanks for the improvements in the date getSubmitValue() function, i will incorporate that.
@Oleg: Yes it should read plugins: instead of plugin:. That was a typo.

Regarding the fileUpload problem: Yes you are right, thats a problem. Until now I simply did not have to deal with file uploads. But for file uploding, I would rather use one of the various file upload extensions floating around in the ext forum.

Error when used with a REST style rails backend => When I post a modified record, if configured params: { _method: ‘PUT’ } rails should recognize the request as PUT and route the request to a controller’s update method.
Can you fix this? ( the orignal submit method hv no such problem)

@Infinit: Sorry I do not see your problem. As the original submit action, i use the getMethod() call to retrieve the method to use. OOSubmit calls the original getMethod of the submit action so it should work exactly as the submit action.
Why do you use “_method” instead of “method” to specify the method? In my opinion this would never work, since getMethod() only looks at the following places to find the method:
this.options.method
this.form.method
this.form.el.dom.method

Is this perhaps a typo?

Ah, now i see the problem. A fix is underway…

I have now updated the post, and fixed/improved various parts of the code. Thanks for all the feedback!

Hi, thanks for this great extension.
Another fix you could include would be a workaround for the fact that you cannot currently have form fields named as arrays, ie foo[bar]. This currently throws an exception on submission!

Overall I like this approach. However, it does not submit the correct values to the server for radio button groups. It sends true/false for the last button instead of the inputValue for the one that is selected.

Disregard my last post about the incorrect radio values being sent to the server. The Ext.ux.RadioGroup extension fixes this.

Can’t figure out how to enable:

standardSubmit:true,

using OOSubmit. So far, it just gets ignored.

I also don’t understand the difference between using the plugin version vs not using the plugin version, or is there only one configuration for OOSubmit?

Seems like i need to modify the submit method to recognize this option, but every time I try, either I break OOSubmit and it no longer submits correct values, or the submit override is ignored and this option is never seen.

Could use a clue how to go about this, with a working example to examine.

thx, Jeff Papineau

hiddenName works too, for combo values and STANDARD submit, instead of using OOSubmit:
items: [{ xtype:’selectbox’, id:’rt_air_class’,
valueField:’value’, displayField:’display’, hiddenName: ‘rt_air_class’, <<< HERE

@surfyogi:
standardSubmit is not compatible with OOSubmit because of the nature of OOSubmit.
The only way to get it to work would be, iterating over all fields in the submit method, getting their submitValue and setting that as value of the input-tag, and then submit the form the traditional way. This would however mean that values would be “flashing” prior to submit, because they would be changed.
And I even dont know, if it would work for comboboxes.

So sorry, OOSubmit has no solution for your particular problem, but could be the starting point for a custom solution.

The plugin does nothing more than overriding the standard submit() method of the form to use oosubmit instead of submit. Other than that it is the same. This is for example handy when some other code has to submit the form, but this other code does not know that it should use OOSubmit to submit the form. (For example third party code)

Hope that helps!

seems like you saved my life!
thanx!

Would this work in cases when the submitted items aren’t direct items of the form - eg. they are in a fieldset, or inside a tab?
If not, how could this be extended to cases like that?

@ SunWuKung:
This would not work out of the box. Unfortunately I see no easy way to implement that.
However Line 40-50 of the first code-block are the place to look at. It iterates over all items in the form. Maybe it would be possible to add an array of other forms / panels whose items should be collected into the options object of the submit method. But this could create problems when Ext is parsing the result of the request and tries to find the fields to display the errorText. Sorry, but I don’t know how Ext handles such more complex forms…

Do you see any problems with using a recursive function to traverse the form? - go through all items, if it has items go through them as well

If you think that can work, we may give it a try - if I can convince my fellow here, since I can’t write code :)

Yes I think this would be doable, but then you would not have a traditional form that gets submitted. Because forms cannot include sub-items that are not form-items.
But it would be surely possible to write a plugin that could be attached to any panel, and that recursively goes through all items and checks wether items are form-items and collects their data.
This data could then be transmitted over a custom request. You should then parse the answer of the “save” manually and show the user hints about fields that failed.
This whole thing would then not be a submit action anymore, but a plugin attachable to any panel, that would add a method getFormData() which would in turn collect the values of all fields in the panel.
- Certainly doable, but don’t copy to much code from this plugin, rather try to understand the code, and rewrite it from scratch for your needs.

You are a prince among thieves. Thank you for posting this code.