Click here to Skip to main content
Click here to Skip to main content

Automatically Submit Form Data

, 17 May 2009
Rate this:
Please Sign up or sign in to vote.
Automatically submit form data using ASP.NET and JavaScript.

Introduction

In this article, I explain how to save user input data automatically when filling forms. This functionality is similar to ASP.NET profiling. This is useful for large web forms and if the user closes the browser without saving the form. Here, I also use ASP.NET caching to retrieve data for authenticated users.

Overview

JavaScript is used to bind events on the client side, collect user input data, and also to fill a hash table with user input. On a given period, the hash table is serialized and sent as a query string to the ASPX page using an XmlHttp object. AutoSave.aspx populates an in-memory object with a query string value and on session timeout, values are saved to the database.

Using the code

  1. Bind the OnBlur event of all input controls to fill a hash table when user inputs are given.
  2. Submit all user input data to the server side.
  3. Submit all data to the server side just before the user closes the browser.
//Default.aspx 
<script src="jshashtable.js" type="text/javascript"></script> 
<script type="text/javascript"> 

window.onload = bindEvents;
function bindEvents()
{
    var textBoxes = document.getElementsByTagName("input");
    for (i=0; i< textBoxes.length; i++)
    {
        if (textBoxes[i].type == 'text' || textBoxes[i].type == 'radio')
        {
            textBoxes[i].onblur = updateHashTable;
        }
    }
    
    for (i=0; i< textBoxes.length; i++)
    {
        if (textBoxes[i].type == 'checkbox')
        {
            textBoxes[i].onblur = updateHashTableforCheckBox;
        }
    }
    
    var comboBoxes = document.getElementsByTagName("select");
    for (j=0; j< comboBoxes.length; j++)
    {
        comboBoxes[j].onchange = updateHashTableforCombo;
    }
}

var Data = new Hashtable();
function updateHashTable()
{
    Data.put(this.id, this.value);
    // alert(this.id);
}
        
function updateHashTableforCheckBox()
{
    Data.put(this.id, this.checked);
}

function updateHashTableforCombo()
{
    Data.put(this.id, this.options(this.selectedIndex).value);
}
 
function AutoSave()
{
    if(!Data.isEmpty())
    {
        // qstring = Data.keys();
        qstring = Data.toQueryString();
        //alert ("Key:"+ qstring[0]);
        //SendXmlHttpRequest("GET", "AutoSave.aspx?" + 
        //                   qstring.substring(0,qstring.length-1)); 
        SendXmlHttpRequest("AutoSave.aspx?"+qstring)
        Data.clear();
    }
}
        
var xmlHttp;
function SendXmlHttpRequest(url)
{
    xmlhttp=null;
    if (window.XMLHttpRequest)
    {
        // code for IE7, Firefox, Opera, etc.
        xmlhttp=new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
        // code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    if (xmlhttp!=null)
    {
        xmlhttp.onreadystatechange=state_Change;
        xmlhttp.open("GET",url,true);
        xmlhttp.send(null);
    }
    else
    {
        alert("Your browser does not support XMLHTTP.");
    }
}

function state_Change()
{
    if (xmlhttp.readyState==4)
    {
        // 4 = "loaded"
        if (xmlhttp.status==200)
        {
            // 200 = "OK"
            var textBoxes = document.getElementsByTagName("input");
            for (i=0; i< textBoxes.length; i++)
            {
                textBoxes[i].id.innerHTML=xmlhttp.status;
            }
        }
        else
        {
        alert("Problem retrieving XML data:" + xmlhttp.statusText);
        }
    }
}
 
window.setInterval(AutoSave, 10000);
window.onbeforeunload = AutoSave;

</script>

// Jshashtable.js  

var Hashtable = (function() {
    function isUndefined(obj) {
        return (typeof obj === "undefined");
    }

    function isFunction(obj) {
        return (typeof obj === "function");
    }

    function isString(obj) {
        return (typeof obj === "string");
    }

    function hasMethod(obj, methodName) {
        return isFunction(obj[methodName]);
    }
    
    function hasEquals(obj) {
        return hasMethod(obj, "equals");
    }

    function hasHashCode(obj) {
        return hasMethod(obj, "hashCode");
    }

    function keyForObject(obj) {
        if (isString(obj)) {
            return obj;
        } else if (hasHashCode(obj)) {
            // Check the hashCode method really has returned a string
            var hashCode = obj.hashCode();
            if (!isString(hashCode)) {
                return keyForObject(hashCode);
            }
            return hashCode;
        } else if (hasMethod(obj, "toString")) {
            return obj.toString();
        } else {
            return String(obj);
        }
    }

    function equals_fixedValueHasEquals(fixedValue, variableValue) {
        return fixedValue.equals(variableValue);
    }

    function equals_fixedValueNoEquals(fixedValue, variableValue) {
        if (hasEquals(variableValue)) {
            return variableValue.equals(fixedValue);
        } else {
            return fixedValue === variableValue;
        }
    }

    function equals_equivalence(o1, o2) {
        return o1 === o2;
    }

    function arraySearch(arr, value, arrayValueFunction, 
             returnFoundItem, equalityFunction) {
        var currentValue;
        for (var i = 0, len = arr.length; i < len; i++) {
            currentValue = arr[i];
            if (equalityFunction(value, arrayValueFunction(currentValue))) {
                return returnFoundItem ? [i, currentValue] : true;
            }
        }
        return false;
    }

    function arrayRemoveAt(arr, idx) {
        if (hasMethod(arr, "splice")) {
            arr.splice(idx, 1);
        } else {
            if (idx === arr.length - 1) {
                arr.length = idx;
            } else {
                var itemsAfterDeleted = arr.slice(idx + 1);
                arr.length = idx;
                for (var i = 0, len = itemsAfterDeleted.length; i < len; i++) {
                    arr[idx + i] = itemsAfterDeleted[i];
                }
            }
        }
    }

    function checkKeyOrValue(kv, kvStr) {
        if (kv === null) {
            throw new Error("null is not a valid " + kvStr);
        } else if (isUndefined(kv)) {
            throw new Error(kvStr + " must not be undefined");
        }
    }

    var keyStr = "key", valueStr = "value";    

    function checkKey(key) {
        checkKeyOrValue(key, keyStr);
    }

    function checkValue(value) {
        checkKeyOrValue(value, valueStr);
    }

    /*------------------------------------------------------------------------*/    

    function Bucket(firstKey, firstValue, equalityFunction) {
        this.entries = [];
        this.addEntry(firstKey, firstValue);
        
        if (equalityFunction !== null) {
            this.getEqualityFunction = function() {
                return equalityFunction;
            };
        }
    }

    function getBucketEntryKey(entry) {
        return entry[0];
    }

    function getBucketEntryValue(entry) {
        return entry[1];
    }

    Bucket.prototype = {
        getEqualityFunction: function(searchValue) {
            if (hasEquals(searchValue)) {
                return equals_fixedValueHasEquals;
            } else {
                return equals_fixedValueNoEquals;
            }
        },

        searchForEntry: function(key) {
            return arraySearch(this.entries, key, getBucketEntryKey, 
                   true, this.getEqualityFunction(key));
        },

        getEntryForKey: function(key) {
            return this.searchForEntry(key)[1];
        },

        getEntryIndexForKey: function(key) {
            return this.searchForEntry(key)[0];
        },
        
        removeEntryForKey: function(key) {
            var result = this.searchForEntry(key);
            if (result) {
                arrayRemoveAt(this.entries, result[0]);
                return true;
            }
            return false;
        },

        addEntry: function(key, value) {
            this.entries[this.entries.length] = [key, value];
        },

        size: function() {
            return this.entries.length;
        },

        keys: function(keys) {
            var startIndex = keys.length;
            for (var i = 0, len = this.entries.length; i < len; i++) {
                keys[startIndex + i] = this.entries[i][0];
            }
        },

        values: function(values) {
            var startIndex = values.length;
            for (var i = 0, len = this.entries.length; i < len; i++) {
                values[startIndex + i] = this.entries[i][1];
            }
        },

        containsKey: function(key) {
            return arraySearch(this.entries, key, getBucketEntryKey, 
                   false, this.getEqualityFunction(key));
        },

        containsValue: function(value) {
            return arraySearch(this.entries, value, 
                   getBucketEntryValue, false, equals_equivalence);
        }
    };

    /*------------------------------------------------------------------------*/    

    function BucketItem() {}
    BucketItem.prototype = [];

    // Supporting functions for searching hashtable bucket items

    function getBucketKeyFromBucketItem(bucketItem) {
        return bucketItem[0];
    }

    function searchBucketItems(bucketItems, bucketKey, equalityFunction) {
        return arraySearch(bucketItems, bucketKey, 
               getBucketKeyFromBucketItem, true, equalityFunction);
    }

    function getBucketForBucketKey(bucketItemsByBucketKey, bucketKey) {
        var bucketItem = bucketItemsByBucketKey[bucketKey];

        // Check that this is a genuine bucket item and not something
        // inherited from prototype
        if (bucketItem && (bucketItem instanceof BucketItem)) {
            return bucketItem[1];
        }
        return null;
    }
    
    /*------------------------------------------------------------------------*/    
    
    function Hashtable(hashingFunction, equalityFunction) {
        var bucketItems = [];
        var bucketItemsByBucketKey = {};

        hashingFunction = isFunction(hashingFunction) ? hashingFunction : keyForObject;
        equalityFunction = isFunction(equalityFunction) ? equalityFunction : null;

        this.put = function(key, value) {
            checkKey(key);
            checkValue(value);
            var bucketKey = hashingFunction(key);

            // Check if a bucket exists for the bucket key
            var bucket = getBucketForBucketKey(bucketItemsByBucketKey, bucketKey);
            if (bucket) {
                // Check this bucket to see if it already contains this key
                var bucketEntry = bucket.getEntryForKey(key);
                if (bucketEntry) {
                    // This bucket entry is the current mapping of key to value, so replace
                    // old value and we're done.
                    bucketEntry[1] = value;
                } else {
                    // The bucket does not contain an entry for this key, so add one
                    bucket.addEntry(key, value);
                }
            } else {
                // No bucket, so create one and put our key/value mapping in
                var bucketItem = new BucketItem();
                bucketItem[0] = bucketKey;
                bucketItem[1] = new Bucket(key, value, equalityFunction);
                bucketItems[bucketItems.length] = bucketItem;
                bucketItemsByBucketKey[bucketKey] = bucketItem;
            }
        };

        this.get = function(key) {
            checkKey(key);

            var bucketKey = hashingFunction(key);

            // Check if a bucket exists for the bucket key
            var bucket = getBucketForBucketKey(bucketItemsByBucketKey, bucketKey);
            if (bucket) {
                // Check this bucket to see if it contains this key
                var bucketEntry = bucket.getEntryForKey(key);
                if (bucketEntry) {
                    // This bucket entry is the current mapping of key to value, so return
                    // the value.
                    return bucketEntry[1];
                }
            }
            return null;
        };

        this.containsKey = function(key) {
            checkKey(key);

            var bucketKey = hashingFunction(key);

            // Check if a bucket exists for the bucket key
            var bucket = getBucketForBucketKey(bucketItemsByBucketKey, bucketKey);
            if (bucket) {
                return bucket.containsKey(key);
            }

            return false;
        };

        this.containsValue = function(value) {
            checkValue(value);
            for (var i = 0, len = bucketItems.length; i < len; i++) {
                if (bucketItems[i][1].containsValue(value)) {
                    return true;
                }
            }
            return false;
        };

        this.clear = function() {
            bucketItems.length = 0;
            bucketItemsByBucketKey = {};
        };

        this.isEmpty = function() {
            return bucketItems.length === 0;
        };

        this.keys = function() {
            var keys = [];
            for (var i = 0, len = bucketItems.length; i < len; i++) {
                bucketItems[i][1].keys(keys);
            }
            return keys;
        };

        this.toQueryString = function ()
        {
            var result="";
            var keys = [];
            var values = [];
           
            for (var i = 0, len = bucketItems.length; i < len; i++)
            {
              //var re = bucketItems[i][1].values(values)
              result += bucketItems[i][1].entries[0][0]+
                "="+bucketItems[i][1].entries[0][1]+"&"
            }
            result = result.substring(0,result.length-1);
            return result;
        }

        this.values = function() {
            var values = [];
            for (var i = 0, len = bucketItems.length; i < len; i++) {
                bucketItems[i][1].values(values);
            }
            return values;
        };

        this.remove = function(key) {
            checkKey(key);

            var bucketKey = hashingFunction(key);

            // Check if a bucket exists for the bucket key
            var bucket = getBucketForBucketKey(bucketItemsByBucketKey, bucketKey);

            if (bucket) {
                // Remove entry from this bucket for this key
                if (bucket.removeEntryForKey(key)) {
                    // Entry was removed, so check if bucket is empty
                    if (bucket.size() === 0) {
                        // Bucket is empty, so remove it
                        var result = searchBucketItems(bucketItems, bucketKey, 
                                     bucket.getEqualityFunction(key));
                        arrayRemoveAt(bucketItems, result[0]);
                        delete bucketItemsByBucketKey[bucketKey];
                    }
                }
            }
        };

        this.size = function() {
            var total = 0;
            for (var i = 0, len = bucketItems.length; i < len; i++) {
                total += bucketItems[i][1].size();
            }
            return total;
        };
    }

    return Hashtable;
})();

The server-side code:

//Default.aspx.cs 


protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        UserData userData;
        if (Cache[Context.User.Identity.Name] == null)
        {
            userData = new UserData();
            Cache.Insert(Context.User.Identity.Name, userData,
              null, Cache.NoAbsoluteExpiration,
              
            TimeSpan.FromMinutes(Session.Timeout),

            CacheItemPriority.Default, new CacheItemRemovedCallback(CacheExpired));
        }
        else
        {
            userData = Cache[Context.User.Identity.Name] as UserData;
        }
        
        FillPage(userData); //to populate web form controls with values from DTO
    }
}


internal void CacheExpired(string key, object val, CacheItemRemovedReason reason)
{
    if (reason != CacheItemRemovedReason.Removed)
    {
        //Save.aspx invokes userData.updateDB() to update data in database
        HttpContext.Current.Server.Execute("Save.aspx", new StringWriter());
    }
}

public void FillPage(UserData userData)
{
    //Add code here..
} 

AutoSave.aspx.cs 

protected void Page_Load(object sender, EventArgs e)
{
    UserData userData = Cache[Context.User.Identity.Name] as UserData;
    for (int i = 0; i < Request.QueryString.Count; i++)
    {
        try
        {
            userData[Request.QueryString.GetKey(i)] = Request.QueryString.Get(i);
        }
        catch (Exception ex)
        {
            continue;
        }
    }

    Cache[Context.User.Identity.Name] = userData;
}

UserData.cs

public class UserData
{
    public UserData()
    {
        //
        // TODO: Add constructor logic here
        //
    }

    public string this[string paramName]
    {// string indexer
        get
        {
            return this.GetType().GetProperty(paramName).GetValue(this, null).ToString();
        }
        set
        {
            this.GetType().GetProperty(paramName).SetValue(this, value, null);
            
        }
    }
    
    private string _LastName;
    public string LastName
    {
        get { return _LastName; }
        set { _LastName = value; }
    }

    private string _FirstName;
    public string FirstName
    {
        get { return _FirstName; }
        set { _FirstName = value; }
    }


    private string _Email;
    public string Email
    {
        get { return _Email; }
        set { _Email = value; }
    }


    public void updateDB()
    {

        /***here: invoke Stored Procedure to update data in database***/

        HttpContext.Current.Cache.Remove(HttpContext.Current.User.Identity.Name);
    }

}

Limitations

  1. If the page form is automatically filled by a web-form-filler software, this won't work.
  2. In modern browsers, query string support is only up to 2000 chars; data loss happens if that length is exceeded.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Md.Asaduzzaman Azad
Data Path Limited
Bangladesh Bangladesh
No Biography provided

Comments and Discussions

 
GeneralVery Nice PinmemberJeffCirceo18-May-09 8:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 17 May 2009
Article Copyright 2009 by Md.Asaduzzaman Azad
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid