305

I want to do some pre-server-validation of a form in a Backbone.js model. To do this I need to get the user input from a form into usable data. I found three methods to do this:

  1. var input = $("#inputId").val();
  2. var input = $("form.login").serialize();
  3. var input = $("form.login").serializeArray();

Unfortunately, none of the provide a good reabable and developable JSON object which I require. I already looked through several questions on Stack Overflow, but I found only some extra libraries.

Doesn't Underscore.js, the current jQuery or Backbone.js provide a helper method?

I can't imagine there is no request for such a function.

HTML

<form class="login">
    <label for="_user_name">username:</label>
    <input type="text" id="_user_name" name="user[name]" value="dev.pus" />
    <label for="_user_pass">password:</label>
    <input type="password" id="_user_pass" name="user[pass]" value="1234" />
    <button type="submit">login</button>
</form>

JavaScript

var formData = $("form.login").serializeObject();
console.log(formData);

Outputs

{
    "name": "dev.pus",
    "pass": "1234"
}

Backbone.js model

var user = new User(formData);
user.save();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dev.pus
  • 7,919
  • 13
  • 37
  • 51
  • 1
    jQuery has a plugin for jSON: http://code.google.com/p/jquery-json/, it does not include a helper method. – nilgun Jul 05 '12 at 06:26
  • 5
    What's wrong with `.serializeArray()`? – xdazz Jul 05 '12 at 06:30
  • 2
    I need it for forms, I am just wondering that three frameworks don't provide a form mapper... – dev.pus Jul 05 '12 at 06:31
  • 3
    About duplicate mark `This question already has an answer here:`: **JSON is not JavaScript object, its serialized object**! – Alex78191 Jun 11 '17 at 21:01
  • JavaScript Object Notation - JSON – M H Jun 13 '17 at 15:15
  • Have you check this : https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery/39248551#39248551 – Bhavik Hirani Mar 26 '18 at 08:00
  • 11
    @xdazz maybe they want { "name": "value" } instead of { "name": "input_name", "value": "input_value" } – gonatee Mar 26 '18 at 10:17
  • i found this useful http://benalman.com/code/projects/jquery-bbq/examples/deparam/ – Ahmed Ali Mar 07 '20 at 07:41
  • 1
    const contactFormData = contactForm.serializeArray() .reduce(function (accumObj, { name, value }) { return { ...accumObj, [name]: value} }, {}); – uak Sep 26 '20 at 15:13
  • I wonder why people here keep dictating what the OP wants. I interpret it as tantrums for not knowing the right answer. Anyway... – Jhourlad Estrella Nov 22 '20 at 14:36
  • this will work exact what you expect, execute following lines once: $.fn.serializeObject = function(){ let d={}; $(this).serializeArray().forEach(r=>d[r.name]=r.value); return d; } now you can call $('#form').serializeObject() – Dharmendrasinh Chudasama Oct 21 '22 at 13:41

14 Answers14

313

Here's a function for this use case:

function getFormData($form){
    var unindexed_array = $form.serializeArray();
    var indexed_array = {};

    $.map(unindexed_array, function(n, i){
        indexed_array[n['name']] = n['value'];
    });

    return indexed_array;
}

Usage:

var $form = $("#form_data");
var data = getFormData($form);
chim
  • 8,407
  • 3
  • 52
  • 60
Maciej Pyszyński
  • 9,266
  • 3
  • 25
  • 28
  • it is getting closer. I have updated the starting thread how I thought it should look like... – dev.pus Jul 05 '12 at 07:17
  • 9
    The codes overwrites keys with the same name. You should check if indexed_array[n['name']] already exists and if it convert it to array and add the n['value'] there. Of course you also need to check if indexed_array[n['name']] is already an array. – Strix May 22 '13 at 22:13
  • 19
    name should be ALWAYS unique (radio buttons, can have only one value!), and if it's not - only last occurrence of field with repeated name is send with request. If you want create multilevel array structure, you have to detect square brackets and from that info build multidimensional arrays. Tip: wrap first part of field name with brackets e.g. `somefield[2] => [somefield][2]` and use eval to assign value `eval('you_array_varialbe'+bracketed_field_name+'=\''+n['value']+'\'')` – Maciej Pyszyński May 23 '13 at 13:20
  • 35
    "name should be ALWAYS unique"... Ummm... checkbox group? – Jeff Lowery Jun 05 '15 at 00:19
  • Yes checkbox group too. You got [] on the end of name, so it's auto-numerated. Auto-numeration results in unique name;), – Maciej Pyszyński Jun 05 '15 at 06:00
  • 6
    Nice, this should be marked as the correct answer. – Ricardo Vigatti Mar 16 '16 at 16:27
  • 1
    CSHTML multiselect list seems to use the same name multiple selected items, @Strix might be right in some cases – Daniël Camps Nov 14 '17 at 14:17
  • @DanielCamps no, his not right. Read about HTML and forms – Maciej Pyszyński Nov 15 '17 at 22:00
  • 1
    Great function, just remember you need to JSON.stringify() before you send it, as is, its an object. – Mfoo Mar 03 '18 at 15:53
  • 5
    @DaniëlCamps is right. A [ – Vanderlei Morais Oct 04 '18 at 17:25
  • 1
    @MaciejPyszyński asp.net mvc model binder uses the same names, such as checkbox lists. It's then automatically binded to viewmodel as Enumerable object so saying "ALWAYS" is a bit misleading. – Sean T Feb 14 '19 at 11:46
  • @SeanT ALWAYS is not missleading:) because it's correct: When you got [] at the end of the name this field is auto-numerated, so having something[] = 'one', something[] = 'two', will result in something[0] = 'one', something[1] = 'two' – Maciej Pyszyński Mar 01 '19 at 12:03
  • 1
    @MaciejPyszyński I think we're disagreeing on semantics to be fair. If I have 3 fields named `Something[]` that to me is not unique - sure the model binder will make it unique by adding the index, but purely from a typing out the markup perspective I've not created a unique name – Sean T Mar 01 '19 at 12:06
  • i had trouble submitting form data as JSON for to my node/express endpoint and this solved it! ty – Vladimir Mujakovic May 27 '19 at 04:20
  • 2
    If you have more than one checkbox with the same name, you might use this block: ```if (indexed_array[n['name']] !== undefined) { indexed_array[n['name']] += ';;;' + n['value']; } else { indexed_array[n['name']] = n['value']; }``` – BonisTech Mar 17 '21 at 10:37
  • This creates the name without quotes. Example {comment: "This is a comment",..} How can it create the name inside quotes also? Example {"comment": "dsfadsfsd",..} – Arya Jun 13 '21 at 21:04
  • 1
    @Jonas Merhej thanks, the block worked for multiplie values – Yehia Elhawary Aug 30 '21 at 15:03
  • SHAME ON JQUERY, this should've been a built-in functionality by now – ttvd94 Nov 16 '21 at 01:11
174

You can do this:

function onSubmit( form ){
  var data = JSON.stringify( $(form).serializeArray() ); //  <-----------

  console.log( data );
  return false; //don't submit
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<form onsubmit='return onSubmit(this)'>
  <input name='user' placeholder='user'><br>
  <input name='password' type='password' placeholder='password'><br>
  <button type='submit'>Try</button>
</form>

see this: http://www.json.org/js.html

vsync
  • 118,978
  • 58
  • 307
  • 400
Adil Shaikh
  • 44,509
  • 17
  • 89
  • 111
  • 73
    Hi, not quite. I am getting something like { "name": "user[name]" value: "dev.pus" } but I want { "name": "dev.pus", "password": "1234" } etc. – dev.pus Jul 05 '12 at 06:30
  • 2
    @dev.pus: You might need to handle it in the [{"name":"username","value":"foo"}] format. Otherwise, you cannot handle multiple elements with the same name. – Matthew Schinckel Apr 26 '13 at 08:19
  • 15
    if you didnt have multiple elements with same names you could easily translate form data to a JSON string like this: var myjson = {}; $.each(allFormTags, function() { myjson[this.name] = this.value; }); – Timothy Sep 23 '13 at 03:28
  • 2
    As already mentioned, this gives `name=foo value=boo` pairs instead of just `foo=boo` - answer from @Maciej Pyszyński seems more appropriate – andreister Jul 03 '14 at 12:40
  • 6
    This doesn't work well..:3 – Zugor Jun 01 '17 at 07:15
  • 2
    This is not the correct answer despite the high number of upvotes – Rigel Sep 20 '18 at 08:30
  • this is not JSON object – Malek Tubaisaht Jun 06 '21 at 20:37
87

The below code should help you out. :)

 //The function is based on http://css-tricks.com/snippets/jquery/serialize-form-to-json/
 <script src="//code.jquery.com/jquery-2.1.0.min.js"></script>

<script>
    $.fn.serializeObject = function() {
        var o = {};
        var a = this.serializeArray();
        $.each(a, function() {
            if (o[this.name]) {
                if (!o[this.name].push) {
                    o[this.name] = [o[this.name]];
                }
                o[this.name].push(this.value || '');
            } else {
                o[this.name] = this.value || '';
            }
        });
        return o;
    };

    $(function() {
        $('form.login').on('submit', function(e) {
          e.preventDefault();

          var formData = $(this).serializeObject();
          console.log(formData);
          $('.datahere').html(formData);
        });
    });
</script>
STEEL
  • 8,955
  • 9
  • 67
  • 89
  • 5
    This function is not only 'based on', it *is* the function from that link. Good thing that [CSS tricks gives you permission](https://css-tricks.com/license/). – Martijn Pieters Sep 26 '19 at 21:08
  • This is great! also working with MVC forms, because the checkboxes are rendered with 2 inputs (one hidden with value false) and with this data serialization, the controller on the other side works out of the box.... – BRap Oct 09 '19 at 09:12
  • This code won't work for inputs like "fields[key1] = value1", don't use it in production! – Hristo93 Aug 06 '20 at 07:51
46

Use:

var config = {};
jQuery(form).serializeArray().map(function(item) {
    if ( config[item.name] ) {
        if ( typeof(config[item.name]) === "string" ) {
            config[item.name] = [config[item.name]];
        }
        config[item.name].push(item.value);
    } else {
        config[item.name] = item.value;
    }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Maertz
  • 4,952
  • 2
  • 18
  • 27
37

I know this doesn't meet the helper function requirement, but the way I've done this is using jQuery's $.each() method

var loginForm = $('.login').serializeArray();
var loginFormObject = {};
$.each(loginForm,
    function(i, v) {
        loginFormObject[v.name] = v.value;
    });

Then I can pass loginFormObject to my backend, or you could create a userobject and save() it in backbone as well.

ryanday
  • 2,506
  • 18
  • 25
  • I tried to use this for myself as a workaround for other problems, but I found a problem here. On a website done via ASP.NET MVC (Razor style view), the code generates a visible checkbox input and a hidden input. Using your code, `loginForm` contained both inputs (if the checkbox was checked, `false` only otherweise) and `loginFormObject` afterwards got the second input, which was always `false`. Solution here: in the sub-function of the `each`, add the following line after the `{`: `if(formObject[v.name] != "true")`. This will preserve the `true`. – MilConDoin Oct 19 '16 at 12:05
17

Trying to solve the same problem (validation without getting into complex plugins and libraries), I created jQuery.serializeJSON, that improves serializeArray to support any kind of nested objects.

This plugin got very popular, but in another project I was using Backbone.js, where I would like to write the validation logic in the Backbone.js models. Then I created Backbone.Formwell, which allows you to show the errors returned by the validation method directly in the form.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tothemario
  • 5,851
  • 3
  • 44
  • 39
13

I couldn't find an answer that would solve this:

[{name:"Vehicle.Make", value: "Honda"}, {name:"Vehicle.VIN", value: "123"}]

This calls for this object:

{Vehicle: {Make: "Honda", "VIN": "123"}}

So I had to write a serializer of my own that would solve this:

function(formArray){
        var obj = {};
        $.each(formArray, function(i, pair){
            var cObj = obj, pObj, cpName;
            $.each(pair.name.split("."), function(i, pName){
                pObj = cObj;
                cpName = pName;
                cObj = cObj[pName] ? cObj[pName] : (cObj[pName] = {});
            });
            pObj[cpName] = pair.value;
        });
        return obj;
    }

Maybe it will help somebody.

Code Uniquely
  • 6,356
  • 4
  • 30
  • 40
user3664916
  • 541
  • 6
  • 10
11

If you do not care about repetitive form elements with the same name, then you can do:

var data = $("form.login").serializeArray();
var formData = _.object(_.pluck(data, 'name'), _.pluck(data, 'value'));

I am using Underscore.js here.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mitar
  • 6,756
  • 5
  • 54
  • 86
7

If you are sending the form with JSON you must remove [] in the sending string. You can do that with the jQuery function serializeObject():

var frm = $(document.myform);
var data = JSON.stringify(frm.serializeObject());

$.fn.serializeObject = function() {
    var o = {};
    //    var a = this.serializeArray();
    $(this).find('input[type="hidden"], input[type="text"], input[type="password"], input[type="checkbox"]:checked, input[type="radio"]:checked, select').each(function() {
        if ($(this).attr('type') == 'hidden') { //if checkbox is checked do not take the hidden field
            var $parent = $(this).parent();
            var $chb = $parent.find('input[type="checkbox"][name="' + this.name.replace(/\[/g, '\[').replace(/\]/g, '\]') + '"]');
            if ($chb != null) {
                if ($chb.prop('checked')) return;
            }
        }
        if (this.name === null || this.name === undefined || this.name === '')
            return;
        var elemValue = null;
        if ($(this).is('select'))
            elemValue = $(this).find('option:selected').val();
        else elemValue = this.value;
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(elemValue || '');
        } else {
            o[this.name] = elemValue || '';
        }
    });
    return o;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user1990497
  • 79
  • 1
  • 1
7

Here is what I use for this situation as a module (in my formhelper.js):

define(function(){
    FormHelper = {};

    FormHelper.parseForm = function($form){
        var serialized = $form.serializeArray();
        var s = '';
        var data = {};
        for(s in serialized){
            data[serialized[s]['name']] = serialized[s]['value']
        }
        return JSON.stringify(data);
    }

    return FormHelper;
});

It kind of sucks that I can't seem to find another way to do what I want to do.

This does return this JSON for me:

{"first_name":"John","last_name":"Smith","age":"30"}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Johnston
  • 20,196
  • 18
  • 72
  • 121
  • are you sure that if a form have multiple values for the same key. This function will only retain the last value? – Lonare Feb 18 '20 at 08:59
6

Using Underscore.js:

function serializeForm($form){
    return _.object(_.map($form.serializeArray(), function(item){return [item.name, item.value]; }));
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
muZk
  • 2,908
  • 1
  • 21
  • 22
  • 4
    This is not a general solution; a form can have multiple values for the same key. This function will only retain the last value. – pimlottc Mar 20 '15 at 19:43
3

Using jQuery and avoiding serializeArray, the following code serializes and sends the form data in JSON format:

$("#commentsForm").submit(function(event){
    var formJqObj = $("#commentsForm");
    var formDataObj = {};
    (function(){
        formJqObj.find(":input").not("[type='submit']").not("[type='reset']").each(function(){
            var thisInput = $(this);
            formDataObj[thisInput.attr("name")] = thisInput.val();
        });
    })();
    $.ajax({
        type: "POST",
        url: YOUR_URL_HERE,
        data: JSON.stringify(formDataObj),
        contentType: "application/json"
    })
    .done(function(data, textStatus, jqXHR){
        console.log("Ajax completed: " + data);
    })
    .fail(function(jqXHR, textStatus, errorThrown){
        console.log("Ajax problem: " + textStatus + ". " + errorThrown);
    });
    event.preventDefault();
});
rbarriuso
  • 787
  • 8
  • 30
1

My contribution:

function serializeToJson(serializer){
    var _string = '{';
    for(var ix in serializer)
    {
        var row = serializer[ix];
        _string += '"' + row.name + '":"' + row.value + '",';
    }
    var end =_string.length - 1;
    _string = _string.substr(0, end);
    _string += '}';
    console.log('_string: ', _string);
    return JSON.parse(_string);
}

var params = $('#frmPreguntas input').serializeArray();
params = serializeToJson(params);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • I don't think it's a good idea to build the JSON string manually when there are built-in functions that do that for you. This is rife with potential errors. – siride Feb 10 '21 at 18:56
1

Well, here's a handy plugin for it: https://github.com/macek/jquery-serialize-object

The issue for it is:

Moving ahead, on top of core serialization, .serializeObject will support correct serializaton for boolean and number values, resulting valid types for both cases.

Look forward to these in >= 2.1.0

Community
  • 1
  • 1
Yaxing
  • 81
  • 1
  • 1
  • 5