Automatically storing changes in a form

This post is more than 2 years old.

Ok, earlier today I complained on Twitter (shocking I know) about a particular web site that I can't mention by name that has a particular 'feature' that drives me bonkers. The site has a list of forums you can subscribe to. When you click the checkbox to confirm you want to subscribe it automatically stores your preference. What bugs me is how it does this. As soon as you click the checkbox (either on or off) the entire form is posted to the server. To make matters worse, this is not the most "zippy" site out there (it's not ColdFusion by the way) so just updating your preferences can take a while and cause issues if you are trying to change something while the page is reloading. It's frustrating and it's ticked me off enough times that I decided to quickly rewrite the UX. Let's start with a mockup of how the site currently works. (I didn't view source on the page but rather built it as I imagined it is built.)

<cfparam name="session.subscribed" default="">

<cfset data = queryNew("name,id","cf_sql_varchar,cf_sql_integer")> <cfset queryAddRow(data)> <cfset querySetCell(data, "name", "Alpha")> <cfset querySetCell(data, "id", 1)> <cfset queryAddRow(data)> <cfset querySetCell(data, "name", "Beta")> <cfset querySetCell(data, "id", 2)> <cfset queryAddRow(data)> <cfset querySetCell(data, "name", "Gamma")> <cfset querySetCell(data, "id", 3)> <cfset queryAddRow(data)> <cfset querySetCell(data, "name", "Delta")> <cfset querySetCell(data, "id", 4)> <cfset queryAddRow(data)> <cfset querySetCell(data, "name", "Enterprise")> <cfset querySetCell(data, "id", 5)>

<cfif structKeyExists(form, "subscribe")> <cfset sleep(3000)> <cfset session.subscribed = form.subscribe> </cfif>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() {

$(".sublist").change(function() {
	$("#selForm").submit();
});

}) </script>

<form method="post" id="selForm"> <table border="1" width="500"> <tr> <td>Name</td> <td width="50">Subscribe</td> </tr> <cfoutput query="data"> <tr> <td>#name#</td> <td><input type="checkbox" class="sublist" name="subscribe" value="#id#" <cfif listFindNoCase(session.subscribed, id)>checked</cfif>></td> </tr> </cfoutput> </table> </form>

So from the top, I begin by initializing a simple Session variable. This variable represents the preferences I want to store. Normally it would be loaded via the database, or perhaps a cookie, but you get the idea. The next set of code simply creates some fake data for us to render. Skip past the JavaScript and look at the table. It renders the fake data with checkboxes to allow for subscriptions. If you go back up to the JavaScript you can see that on each and every change we submit the form. That's the part I dislike. In the form processing I intentionally added a sleep() in there to mimic the slowness of the current site. (Next time I'll just write it in PHP.)

You can view this page here: http://www.coldfusionjedi.com/demos/feb1b2011/test3.cfm Clicking around, especially quickly, causes a lot of page reloads. In some testing I've seen it actually get confused and check things I had just unchecked. Now let's look at a better version.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() {

$(".sublist").change(function() {
	var thisid = $(this).val();
	var sel = $(this).attr("checked");
	$.post("test.cfc?method=storesubscription", {"id":thisid, "subscribe":sel});
});

}) </script>

I've modified the code to grab the ID and checked status from the form field. Now when you click it will fire off an Ajax request to store the update. My CFC just wraps the session update (and I'll post that if folks want but it's just a few List functions) and returns no value. You can play with this here: http://www.coldfusionjedi.com/demos/feb1b2011/test4.cfm. Hopefully you can see that this version feels a lot more fluid. I wrote this in like 5 minutes so if it fails in IE or if it is still 'fragile' then I wouldn't be surprised, but hopefully the concept makes sense and hopefully I'm not alone in thinking this is much improved.

As another version of this - I believe Google's Blogger.com service does something similar when posting comments. So this particular site is not alone in having this UX issue.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Tim Garver posted on 2/1/2011 at 11:57 PM

cool, only issue i see, you cant uncheck a preference

Comment 2 by Raymond Camden posted on 2/1/2011 at 11:59 PM

What browser? Unchecking worked for me.

Comment 3 by Jason Brookins posted on 2/2/2011 at 12:26 AM

I just can't see why anyone would force a form submit/page load in this situation. I mean, isn't this what asynchronous processes were born to handle?

Comment 4 by Dave posted on 2/2/2011 at 12:51 AM

Would you be able to post the cfc?

Comment 5 by Raymond Camden posted on 2/2/2011 at 12:57 AM

Here is the method. TO BE 100% CLEAR: This is NOT how I'd code it normally. This was the 'quickie' solution:

remote void function storesubscription(numeric id, boolean subscribe) {
if(arguments.subscribe) {
if(!listFindNoCase(session.subscribed, arguments.id)) session.subscribed = listAppend(session.subscribed, arguments.id);
} else {
if(listFindNoCase(session.subscribed, arguments.id)) session.subscribed = listDeleteAt(session.subscribed, listFind(session.subscribed,arguments.id));
}
}

Comment 6 by Manithan posted on 2/2/2011 at 1:17 AM

Is there any way i can store as kind of persistent js datastore using jquery instead hitting the server for evert entry ?

Comment 7 by Raymond Camden posted on 2/2/2011 at 1:18 AM

Certainly. You can write to your cookies in JS, or, if your browser is capable, use HTML5's new storage capacity.

Comment 8 by Manithan posted on 2/2/2011 at 1:38 AM

I have huge form (rebate form) which has 100+ fields the client wants to have it as one page form, i did try storing it using cookie by serializing to json but it is not storing all the values. i couldn't debug either, if i open the firebug console jQuery validation gets in to loop and you get message "stop script or continue" popup. may i am running in to the cookie storage limit.

Thanks

Comment 9 by Manithan posted on 2/2/2011 at 1:39 AM

there is no HTML5 option because it has to work from IE7 and up

Comment 10 by Phillip Senn posted on 2/3/2011 at 12:23 AM

I think with a little setInterval() JavaScript, or a setTimeout() being called over and over again, you could make a page look like it was live. I need to get around to doing this, because I have times where I have 20 students answering a poll at the same time. I simply do a page refresh every 30 seconds, but it would be cooler to interactively update the page every few seconds instead. I know there's been discussion of long polling and something called Comet, but I don't know if that's already fallen out of favor or not.

Comment 11 by Raymond Camden posted on 2/3/2011 at 7:11 PM

@Manithan: I think you would need to use Ajax to post the fields to the server and store them there - keyed to a unique ID for the user.

Comment 12 by Manithan posted on 2/4/2011 at 7:02 AM

Thanks Ray.
I have one more question. if i am accessing server for every field changes is that going to use too many threads on the server say if 100 users is entering the form data, will that going to be a performance issue. just thinking out loud.

Comment 13 by Raymond Camden posted on 2/7/2011 at 11:40 PM

You could consider doing the storage ever 5 seconds or so. Then if a user's browser crashes they lose at most 5 seconds of editing.