36

Is there a jQuery plugin out there that can serialize a form, and then later restore/populate the form given the serialized value? I know the form plugin can serialize as a querystring, but I haven't found anything that will restore the form from the querystring.

What I'd like to do is serialize the form values, store as a cookie whenever the form changes, and then restore the form from the cookie (if it exists) when the page first loads.

I have found pieces of this puzzle out there (form plugin, cookie plugin, various autosave plugins that don't restore), but before I cobble something together from various parts, I wanted to make sure there wasn't a nice canned solution waiting for me out there.

Thanks!

Jim

Jim Biancolo
  • 804
  • 2
  • 9
  • 14

8 Answers8

46

Here's a little something I rolled based on work of others, specifically serializeAnything:

/* jQuery.values: get or set all of the name/value pairs from child input controls   
 * @argument data {array} If included, will populate all child controls.
 * @returns element if data was provided, or array of values if not
*/

$.fn.values = function(data) {
    var els = $(this).find(':input').get();

    if(typeof data != 'object') {
        // return all data
        data = {};

        $.each(els, function() {
            if (this.name && !this.disabled && (this.checked
                            || /select|textarea/i.test(this.nodeName)
                            || /text|hidden|password/i.test(this.type))) {
                data[this.name] = $(this).val();
            }
        });
        return data;
    } else {
        $.each(els, function() {
            if (this.name && data[this.name]) {
                if(this.type == 'checkbox' || this.type == 'radio') {
                    $(this).attr("checked", (data[this.name] == $(this).val()));
                } else {
                    $(this).val(data[this.name]);
                }
            }
        });
        return $(this);
    }
};
radicand
  • 6,068
  • 3
  • 27
  • 22
Barnabas Kendall
  • 4,317
  • 1
  • 32
  • 24
  • Wow nice! Will save this for future use. – Thomas R Sep 29 '09 at 03:32
  • Very cool! I'll just have to add a to/from JSON step so I can store the serialized state as a cookie. Thanks! – Jim Biancolo Sep 29 '09 at 14:06
  • Somebody need to make an official plugin from this :) – Dmytrii Nagirniak Feb 21 '12 at 23:26
  • i expanded on this to support multiple checkboxes with same name: http://stackoverflow.com/a/12218972/26188 – mkoryak Aug 31 '12 at 16:07
  • Use $(this).prop() instead of $(this).attr() to set "checked" with jQuery>=1.6. https://api.jquery.com/prop/ http://stackoverflow.com/questions/5874652/prop-vs-attr – ddaa Sep 22 '15 at 08:58
  • 1
    Because empty fields could not be restored, I would change this line `if (this.name && data[this.name]) {` into `if (this.name && typeof data[this.name] != 'undefined') {`. – Hieu Nguyen Oct 19 '16 at 22:31
  • Can someone explain the `data[this.name] == $(this).val()` part for checkbox? I don't get it. – Chin Mar 19 '18 at 20:31
16

Ive expanded upon Barnabas answer with the following:

  1. Support multiple inputs with same name (checkboxes usually do this).
  2. Cache selectors when possible, remove unneeded use of $

        /* jQuery.values: get or set all of the name/value pairs from child input controls   
         * @argument data {array} If included, will populate all child controls.
         * @returns element if data was provided, or array of values if not
        */
    
        $.fn.values = function(data) {
            var els = this.find(':input').get();
    
            if(arguments.length === 0) {
                // return all data
                data = {};
    
                $.each(els, function() {
                    if (this.name && !this.disabled && (this.checked
                                    || /select|textarea/i.test(this.nodeName)
                                    || /text|hidden|password/i.test(this.type))) {
                        if(data[this.name] == undefined){
                            data[this.name] = [];
                        }
                        data[this.name].push($(this).val());
                    }
                });
                return data;
            } else {
                $.each(els, function() {
                    if (this.name && data[this.name]) {
                        var names = data[this.name];
                        var $this = $(this);
                        if(Object.prototype.toString.call(names) !== '[object Array]'){
                            names = [names]; //backwards compat to old version of this code
                        }
                        if(this.type == 'checkbox' || this.type == 'radio') { 
                            var val = $this.val();
                            var found = false;
                            for(var i = 0; i < names.length; i++){
                                if(names[i] == val){
                                    found = true;
                                    break;
                                }
                            }
                            $this.attr("checked", found);
                        } else {
                            $this.val(names[0]);
                        }
                    }
                });
                return this;
            }
        };
    
mkoryak
  • 57,086
  • 61
  • 201
  • 257
  • Could you possibly make a jsfiddle or similar to show in use? I made a simple demo here http://codepen.io/jasondavis/pen/jsmya but I havent got the check/radio boxes working correctly – JasonDavis Apr 28 '13 at 13:48
  • 1
    the reason you cant get it working is because you are doing it wrong :) check/radio buttons have values and thats what you save/set: http://codepen.io/anon/pen/xKizw – mkoryak May 02 '13 at 20:12
  • You should be using `.prop` instead of `.attr` See: http://stackoverflow.com/questions/5874652/prop-vs-attr – chodorowicz Apr 29 '15 at 15:44
  • This is great, for every one using this, remember to add "number" and "email" types too `|| /text|number|email|hidden|password/i.test(this.type))) {` – J. Doe Jul 31 '20 at 03:58
8

Thanks to Barnabas Kendall for initial function and Eggert Jóhannesson for radio button fix!

I encountered an issue with checkboxes, if they are not checked they won't be put into the array, so far so good. But as the state of checkboxes is not stored when they are not checked I couldn't restore this state if the user had checked them during editing the form.

So I extended the restore functionality to uncheck all checkboxes which are not in the data array, this will ensure the state of checkboxes is restored correctly no matter what was changed in the form before executing restore:

if (this.name && data[this.name]) {
   if(this.type == "checkbox" || this.type == "radio") {
       $(this).prop("checked", (data[this.name] == $(this).val()));
   } else {
       $(this).val(data[this.name]);
   }
} else if (this.type == "checkbox") {
   $(this).prop("checked", false);
}

Complete function:

$.fn.values = function(data) {
   var inps = $(this).find(":input").get();

    if(typeof data != "object") {
       // return all data
        data = {};

        $.each(inps, function() {
            if (this.name && (this.checked
                        || /select|textarea/i.test(this.nodeName)
                        || /text|hidden|password/i.test(this.type))) {
                data[this.name] = $(this).val();
            }
        });
        return data;
    } else {
        $.each(inps, function() {
            if (this.name && data[this.name]) {
                if(this.type == "checkbox" || this.type == "radio") {
                    $(this).prop("checked", (data[this.name] == $(this).val()));
                } else {
                    $(this).val(data[this.name]);
                }
            } else if (this.type == "checkbox") {
                $(this).prop("checked", false);
            }
       });
       return $(this);
    }
};
megglos
  • 141
  • 1
  • 3
3
  • Support multiple inputs with same name (checkboxes usually do this).
  • Cache selectors when possible
  • Return values for all inputs, if checkbox or radio isn't set, the value is null
  • Deactivates checkbox or radio if value is null

    $.fn.formData = function(values) {
      var form = $(this);
      var inputs = $(':input', form).get();
      var hasNewValues = typeof values == 'object';
    
      if (hasNewValues) {
        $.each(inputs, function() {
          var input = $(this);
          var value = values[this.name];
    
          if (values.hasOwnProperty(this.name)) {
            switch (this.type) {
              case 'checkbox':
                input.prop('checked', value !== null && value);
                break;
              case 'radio':
                if (value === null) {
                  input.prop('checked', false);
                } else if (input.val() == value) {
                  input.prop("checked", true);
                }
                break;
              default:
                input.val(value);
            }
          }
        });
        return form;
      } else {
        values = {};
        $.each(inputs, function() {
          var input = $(this);
          var value;
          switch (this.type) {
            case 'checkbox':
            case 'radio':
              value = input.is(':checked') ? input.val() : null;
              break;
            default:
              value = $(this).val();
          }
          values[this.name] = value;
        });
        return values;
      }
    };
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
eSlider
  • 181
  • 1
  • 4
3

A big thank you to Barnabas Kendall for his answer, very useful.

However I found 1 error in it regarding restoring radio buttons, where instead of selecting the correct one it just copied the saved value to all buttons in group.

Fortunately it's simple to fix. Just replace

if(this.type == 'checkbox') {

With

if(this.type == 'checkbox' || this.type == 'radio') {

and it will correctly update radio buttons

0

Check out my jQuery Populate plugin:

http://www.keyframesandcode.com/code/development/javascript/jquery-populate-plugin/

Populates all form elements as well as labels and any contained HTML elements.

0

In case you need to handle this case: <input name="this[is][my][input][]" /> - and you, the greedy guy, need the whole matrix parsed:

To populate:

http://www.keyframesandcode.com/resources/javascript/jQuery/demos/populate-demo.html

To retrieve values:

Use $('form').serialize() and pass the results to this function:

http://phpjs.org/functions/parse_str/

Silviu-Marian
  • 10,565
  • 6
  • 50
  • 72
0

To serialize into a string: var s = $('form').first().serialize();

To restore based on that string: $('form').first().deserialize(s);

Of course you'll need a derserialize plugin such as the one I originally posted here.

$.fn.deserialize = function (serializedString) 
{
    var $form = $(this);
    $form[0].reset();
    serializedString = serializedString.replace(/\+/g, '%20');
    var formFieldArray = serializedString.split("&");

    // Loop over all name-value pairs
    $.each(formFieldArray, function(i, pair){
        var nameValue = pair.split("=");
        var name = decodeURIComponent(nameValue[0]);
        var value = decodeURIComponent(nameValue[1]);
        // Find one or more fields
        var $field = $form.find('[name=' + name + ']');

        // Checkboxes and Radio types need to be handled differently
        if ($field[0].type == "radio" || $field[0].type == "checkbox") 
        {
            var $fieldWithValue = $field.filter('[value="' + value + '"]');
            var isFound = ($fieldWithValue.length > 0);
            // Special case if the value is not defined; value will be "on"
            if (!isFound && value == "on") {
                $field.first().prop("checked", true);
            } else {
                $fieldWithValue.prop("checked", isFound);
            } 
        } else { // input, textarea
            $field.val(value);
        }
    });
    return this;
}

More info: https://stackoverflow.com/a/24441276/1766230

Here's a jsfiddle that let's you play around with setting values, clearing, resetting, and "deserializing": http://jsfiddle.net/luken/4VVs3/

Community
  • 1
  • 1
Luke
  • 18,811
  • 16
  • 99
  • 115