Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

jQuery Form Serialization without ASP.NET ViewState


:P
On this page:

If you're doing AJAX callbacks to the server with POST data from ASP.NET pages you might be inclined to do something like this:

var post = $("#form1").serialize();
$.post("MethodCallback.aspx?Callback=ListPanel",
        post, 
        function(result) { alert(result) });

This works fine, but it will pick up all of your ViewState and EventValidation making the post back to the server a heck of a lot bigger than it should be especially if you have a page where ViewState is heavily used. Now I tend to turn off ViewState on most of my pages but you still get at least some ViewState and EventValidation content on the page and in most Ajax scenarios sending that to the server is just wasted bandwidth.

Fortunately it's quite easy to exclude ViewState et al. with code like this:

var res = $("#form1")
                .find("input,textarea,select,hidden")
                .not("#__VIEWSTATE,#__EVENTVALIDATION")
.serialize();

$.post("MethodCallback.aspx?Callback=ListPanel", res, function(result) { alert(result) });

This is a little more verbose, but necessary in order to be able to individually reference each of the child input elements. The #Form1 selector is optional - if you only have a single form or if you don't care about posting all form vars regardless of belonging form, you can just request all input,select,textarea,select and hidden elements.

The .not() function basically filters the result set of input elements and so doesn't send up ViewState and Event validation. If you do this a lot a small generic plug-in might be useful:

$.fn.serializeNoViewState = function()
{
    return this.find("input,textarea,select,hidden")
               .not("[type=hidden][name^=__]")
               .serialize();    
}

which then allows you to do:

var res = $("#form1").serializeNoViewState();

or even the following to select data from all forms:

var res = $().serializeNoViewState();

Reducing ViewState and avoiding it from being sent back and forth between client and server can drastically improve AJAX performance so it's a good idea to be conscious of what goes over the wire on those presumably 'small' AJAX requests. Removing it from the client POST is one quick way to reduce bandwidth.

Note that if you plan on having the page actually go through a normal 'Postback' like stage and you want to capture events during an AJAX callback you can also post back ViewState et al and this can in many cases work, giving you access to POST data on form fields (ie. referencing like this.txtName.Text rather than explicitly calling Request.Form[]). But it's best not to rely on this mechanism as there are lots of little quirks and gotchas. Ajax callbacks are best handled as independently as possible - preferably not back to the same page or a service to avoid any conflicts with ViewState and EventValidation. Otherwise turning ViewState and EventValidation off will help prevent page errors on callbacks or a full Postback following many AJAX updates. This is no trivial matter so you have to understand what's being updated on the client and what will actually make it back to the server (other than form fields). This is why it's best to minimize reliance on Viewstate so that Viewstate corruption and EventValidation won't be a problem.

Encoding POST data via Object Maps

Since we're talking about jQuery POST data encoding - note that you can also use the $.params() function to turn any flat object map into POST data. So you can easily create POST data like this:

var post = { name: "Rick",
    company: "West Wind",
    lastOn: new Date(),
    count: 30
};

$.post("MethodCallback.aspx?CallbackHandle=ListUpdate",
        post,
        function(result) { alert(result) });

which in turn turns all the map values - name, company, lastOn, count - into POST variables that are sent to the server URL encoded. Most places where a 'data' parameter is expected with POST data in jQuery you can either pass a string (as in the first examples above) or an object map as shown here. This is a nice and easy way to create POST data without having to manually worry about POST encoding it.

Behind the scenes jQuery uses the static $.param(data) function which also provides this functionality to your code and lets you turn any object map into a POST string explicitly.

var post = { name: "Rick", email: "rick@morecowbell.com" };
var poststring = $.param(post);

Very useful if you're interacting with REST based applications that work purely on POST data rather than JSON encoded content.

Lots of flexibilty there and it's easy to integrate with ASP.NET.

Posted in ASP.NET  JavaScript  jQuery  

The Voices of Reason


 

Doug
September 04, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

MoreCowbell.com - lol - best skit ever.

Billy McCafferty
September 05, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

Wow, your timing was impeccable! I was just Googling for this a couple days ago.

Tobias H. Michaelsen
September 06, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

I'm not sure, but I would think that jQuery supports the :not() selector, so you could write something like:

$('#form1').find('input:not([name^=__]),...')

I know Prototype.js supports it, so jQuery probably does as well.

Rick Strahl
September 06, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

@Tobias - yes that also works with jQuery - personally I prefer to use an explicit .not() filter because it's easier to read and because there are no limitations on the expressions you can use unlike the inline string representation.

FWIW, the expression as you have it isn't enough - you need to make sure you include input, select, textarea and hidden elements.

Raman Basu
September 07, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

It is excellent! I am using it successfully. Thanks

Dan F
September 07, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

Dude, awesome. Is there anything jQuery can't do?

Walt
September 08, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

Hi Rick, great article as always. I'm trying to implement this on a dynamically created form (the form fields are added using jQuery) and I'm getting this error: "Object doesn't support this property or method" on this line:
postString += $.serialize(prod);
I checked the jQuery documentation and it didn't have any info about using the static serialize function with a parameter like that. Any ideas where that error is coming from? Thanks for all the great info.

Here's the function:
function saveOrder() {
            var paramList = "";
            var postString = "";

            $("#poItems").children(":first").children("tr").each(function() {
                $this = $(this);

                var id = $this.attr("id");
                var desc = $this.children("td:eq(0)").children("input:first").val();
                var quant = $this.children("td:eq(1)").children("input:first").val();
                var price = $this.children("td:eq(2)").children("input:first").val();

                var prod = {
                    prod_id: id,
                    prod_desc: '"' + desc + '"',
                    prod_quant: quant,
                    prod_price: '"' + price + '"' 
                    };
                
                 postString += $.serialize(prod);
            });
            
            alert(postString);
                        
            $.post("MethodCallback.aspx?Callback=UpdateOrder",
                    postString,
                    function(result) {alert(result)}
            );

Rick Strahl
September 08, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

@Walt - my apologies. You're right there's no $.serialize() function on the jQuery object. It's $.param() that does this. Fixed in the post. Thanks for keeping me honest...

Brooke Vigola
September 09, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

So if the page loads with a field for a first name and the user doesn't change it, I don't want to post that field. How can this be accomplished with JQUERY As you know, a form field's value that it initially has resides in its defaultValue property, so I'm just wondering if you could add something like this.

return this.find("input,textarea,select,hidden")
.not("[value=defaultValue]")
.serialize();

Rick Strahl
September 10, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

@Brooke - String selectors on attributes only work with static text expressions. There's no way to look at a dynamic value. So you embed 'default value' as a string in the attribute selector which means you can only compare against a single value.

But you can also use a filter function:

   var defaultValue = "value 1";
   var s = $("input,textarea,select,hidden")
                .filter( function() { 
                   if ( $(this).val() == defaultValue)
                      return false;
                      
                    return true; 
                 });


Of course instead of the static value you could have some logic to define what defaultValue is. Like maybe some custom attribute value?

Stephen
September 13, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

I have been using the jQuery form plugin to easily use its ajaxSubmit and ajaxForm methods. Since you mention conflicts (or gotchas) with ViewState and EventValidation in the case of a full page POST, what issues have you seen?

I have been toying with the ajaxSubmit, and on the response, I simply use jQuery to parse/select the hidden inputs and then stuff their values into the DOM.

Here is a code snipet:

var viewState = $(nodeHtml).find("#__VIEWSTATE").val();
var eventValidation = $(nodeHtml).find("#__EVENTVALIDATION").val();
$("#__VIEWSTATE").val(viewState);
$("#__EVENTVALIDATION").val(eventValidation);


(Where nodeHtml contains the Html text from the response's form Html.)

Using this avoids the problem of getting the "Invalid Event validation" error message if I perform subsequent posts. It also allows me to still perform events on the controls as needed. In your opinion, does this seem like a bad idea?

Also, I'm thinking if you completely disable ViewState, you are effectively disabling the ASP.NET event-driven model. Is there any point in performing a POST to anything other than a WebService from that point forward?

Thanks,
Stephen

Rick Strahl
September 13, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

@Steven - yeah your code will work as long as you're updating EVERYTHING the server side is updating. If you miss something on the client to update you still can get EventValidation errors.

Clever about the ViewState updates. But how do you get the ViewState on the server? Capture full page output?

Turning off ViewState doesn't disable the event model in ASP.NET. The advantage is that you still get form data postback so form vars are set and you potentially get page reloading of state and you can potentially render partial page content from specific controls. There's really little difference of how this works with or without viewstate given that code has to somehow display the page in the proper state whether I let ViewState do it or I reload data.

Ideally though I agree - I prefer using services for data although using a CallbackMethod like approach is often convenient and more organized than having one or a few services that service all pages. Most of the time I start with CallbackMethods and if there are too many switch to a handler/service.

Stephen
September 14, 2008

# re: jQuery Form Serialization without ASP.NET ViewState

@Rick

Updating everything on the client that the service side is updating is exactly what I am doing in some instances. If I perform and capture a full page POST back to the originating ASPX through some method like

var options = { success: processResponse };
$("#formMain").ajaxSubmit(options);

I then iterate through the response like this:
<code lang="javascript">
function processResponse(response, status) {
    var formNode;
        var htmlDoc = $(response).each(function(i, el) {
        if (el.id == formName) {
            formNode = el;
                return;
            }
        });

        var nodeHtml = $(formNode).html();
        $(nodeHtml).each(function(e, el) {
        if (el.id) {
                try {
            var docNode = $("#" + el.id); 
                        docNode.html($(el).html());
                    }
                    catch (e) {
                        alert('Error: ' + e);
                    }
                }
    });
    
    var viewState = $(nodeHtml).find("#__VIEWSTATE").val();
        var eventValidation = $(nodeHtml).find("#__EVENTVALIDATION").val();
        $("#__VIEWSTATE").val(viewState);
        $("#__EVENTVALIDATION").val(eventValidation);
}

I'm sure this is not the best approach, I have not had any issues with it at this point even on complex pages with dynamically created user controls. The main advantage is that I do get an UpdatePanel-like behavior.

Unlike you, I have all sorts of problems when I override OnInit and use this.EnableViewState = false. My events don't fire and I can't seem to retrieve values from DropDownLists. I would definitely like to see an example of where you were disable ViewState, getting your form vars set, and rendering partial page content from specific controls. If I could send back just the control I'm interested in (maybe based on some value I set in a hidden field) through a simple routine like below, that would be sweet:

String controlHtml = ParseControlHtml(Response.ToString(), controlName);
Response.Clear();
Response.Write(controlHtml);
Response.End();


On a side note, if I leave ViewState enabled but then dynamically load my DropDownList (using jQUery/JSON to change the <select> and append <option>...</option>) I get EventValidation errors upon POST. I guess I could look at the AjaxControlToolkit CascadeDropDownExtender to see how they avoid this problem.

Stephen

(Sorry for the double post... but my code tages were in the wrong places)

Christian
February 25, 2009

# re: jQuery Form Serialization without ASP.NET ViewState

Be sure to check our this cool solution for serialization,
http://malsup.com/jquery/form/

Jay
March 19, 2009

# re: jQuery Form Serialization without ASP.NET ViewState

I've used this to serialize an asp:ListBox control, and discovered that I had replace ":" with "_" from the serializeNoViewState() output since serialize() uses the name attribute as the key, which is not available in the code-behind (as far as I know), which means I can now do this to get a comma-separated list of values:

string domains = Request[myListBox.ClientID] ?? "";


Is there a better way? Can I specify my own name attribute?

Jay
March 19, 2009

# re: jQuery Form Serialization without ASP.NET ViewState

I figured it out: UniqueID is the name attribute that is rendered. I simply did this instead for serialize because ":" is escaped for some reason within serialize():

$.fn.serializeNoViewState = function()
{
return this.find("input,textarea,select,hidden")
.not("[type=hidden][name^=__]")
.serialize()
.replace(/%3A/g,':');
}

Then I could do this to get csv of listbox values (which were then passed to a stored procedure using a UDF to put it into a temp table):

string strMyListbox = Request[myListbox_LBX.UniqueID] ?? "";

Mike Gledhill
July 27, 2014

# re: jQuery Form Serialization without ASP.NET ViewState

Wow.
Excellent tip, great time saver.

Donation sent. Go and have a beer !

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024