291

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,030
  • 21
  • 100
  • 124
dev.pus
  • 7,617
  • 12
  • 35
  • 49

15 Answers15

295

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,095
  • 3
  • 49
  • 59
Maciej Pyszyński
  • 8,858
  • 3
  • 23
  • 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
  • 30
    "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 Pires 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']; }``` – Jonas Merhej 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
170

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
  • 103,437
  • 51
  • 275
  • 359
Mohammad Adil
  • 44,013
  • 17
  • 87
  • 109
  • 71
    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; }); – Tim Truston 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
78

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
  • 7,168
  • 8
  • 62
  • 80
44

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,030
  • 21
  • 100
  • 124
Maertz
  • 4,857
  • 2
  • 17
  • 27
35

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,446
  • 18
  • 25
  • 1
    This is nice and simple, and seems to be perfect for my needs right now. Good work! – A Fader Darkly May 17 '16 at 12:42
  • 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
15

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,030
  • 21
  • 100
  • 124
tothemario
  • 5,131
  • 3
  • 38
  • 36
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,226
  • 4
  • 27
  • 40
user3664916
  • 531
  • 5
  • 10
10

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,030
  • 21
  • 100
  • 124
Mitar
  • 6,175
  • 4
  • 48
  • 75
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,030
  • 21
  • 100
  • 124
Johnston
  • 19,388
  • 13
  • 71
  • 111
  • 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

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,030
  • 21
  • 100
  • 124
user1990497
  • 69
  • 1
  • 1
6

Using Underscore.js:

function serializeForm($form){
    return _.object(_.map($form.serializeArray(), function(item){return [item.name, item.value]; }));
}
Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
muZk
  • 2,708
  • 20
  • 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
  • 789
  • 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,030
  • 21
  • 100
  • 124
  • 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
-1

Found one possible helper:

https://github.com/theironcook/Backbone.ModelBinder

and for people who don't want to get in contact with forms at all: https://github.com/powmedia/backbone-forms

I will take a closer look at the first link and than give some feedback :)

dev.pus
  • 7,617
  • 12
  • 35
  • 49
  • See http://stackoverflow.com/questions/1184624/convert-form-data-to-js-object-with-jquery: var data = {}; $(".form-selector").serializeArray().map(function(x){data[x.name] = x.value;}); – Grigory Kislin Apr 22 '15 at 20:06
  • 2
    Here it is the one-liner: $("form.login").serializeArray().reduce( (f,c) => { f[c.name]=c.value; return f;}, {} ); – ShQ Aug 13 '19 at 19:44