marioizquierdo / jquery.serializeJSON

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays.
MIT License
1.71k stars 433 forks source link

Radio Buttons are not working in >3.2.0 #126

Closed MBWebTechMariusz closed 2 years ago

MBWebTechMariusz commented 2 years ago

First, thank you for the great plugin!

I've encountered a breaking bug in v3.2.0 and all following.

Try this code in v3.1.1, and in v.3.2.0:

<form class="form-1">
    <input type="radio" name="input" value="true">
    <input type="radio" name="input" value="false">
</form>

<form class="form-2">
    <input type="radio" name="input" value="true" checked>
    <input type="radio" name="input" value="false">
</form>

<form class="form-3">
    <input type="radio" name="input" value="true">
    <input type="radio" name="input" value="false" checked>
</form>

<form class="form-4">
    <input type="checkbox" name="input" value="checkbox-value">
</form>

<form class="form-5">
    <input type="checkbox" name="input" value="checkbox-value" checked>
</form>

<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js"></script>
<script src="jquery.serializejson.js"></script>

<script>
    var f1 = $('.form-1').serializeJSON();
    console.log('form-1', f1);

    var f2 = $('.form-2').serializeJSON();
    console.log('form-2', f2);

    var f3 = $('.form-3').serializeJSON();
    console.log('form-3', f3);

    var f4 = $('.form-4').serializeJSON();
    console.log('form-4', f4);

    var f5 = $('.form-5').serializeJSON();
    console.log('form-5', f5);

    console.log('Mit settings')

    var settings = {
            useIntKeysAsArrayIndex: true,
            skipFalsyValuesForTypes: ["number"],
            checkboxUncheckedValue: "false"
        };

    var fs1 = $('.form-1').serializeJSON(settings);
    console.log('form-1', fs1);

    var fs2 = $('.form-2').serializeJSON(settings);
    console.log('form-2', fs2);

    var fs3 = $('.form-3').serializeJSON(settings);
    console.log('form-3', fs3);

    var fs4 = $('.form-4').serializeJSON(settings);
    console.log('form-4', fs4);

    var fs5 = $('.form-5').serializeJSON(settings);
    console.log('form-5', fs5);
</script>

v3.1.1 output:

form-1 {}
form-2 {input: 'true'}
form-3 {input: 'false'}
form-4 {}
form-5 {input: 'checkbox-value'}

Mit settings
form-1 {}
form-2 {input: 'true'}
form-3 {input: 'false'}
form-4 {input: 'false'}
form-5 {input: 'checkbox-value'}

v3.2.0 output:

form-1 {}
form-2 {input: 'true'}
form-3 {input: 'false'}
form-4 {}
form-5 {input: 'checkbox-value'}

Mit settings
form-1 {input: 'false'} <-- should be empty
form-2 {input: 'false'} <-- should be true
form-3 {input: 'false'}
form-4 {input: 'false'}
form-5 {input: 'checkbox-value'}

Further remarks:

I think it has something to do that the plugin iterates over every input, and "overwrites" the value if the name is the same. return { name: el.name, value: val.replace(rCRLF, "\r\n"), el: el };

marioizquierdo commented 2 years ago

I think you don't want to use the option checkboxUncheckedValue here. This option is meant for single checkboxes, not for groups of radio buttons.

Older versions like v3.1.1 have a bug when having multiple inputs with the same name, that was fixed on the latest version. You found that bug.

The behavior on the latest version v3.2.0 is actually the expected behavior when using the option checkboxUncheckedValue. See details for unchecked checkboxes in the README: https://github.com/marioizquierdo/jquery.serializeJSON#include-unchecked-checkboxes

Long explanation

Let's focus on form-1 and form-2 to illustrate what is going on:

<form class="form-1">
    <input type="radio" name="input" value="true">
    <input type="radio" name="input" value="false">
</form>
<form class="form-2">
    <input type="radio" name="input" value="true" checked>
    <input type="radio" name="input" value="false">
</form>

Without options, it will serialize based on the standard W3C rules; only including values for input elements that are checked:

$('.form-1').serializeJSON(); // returns => {}
$('.form-2').serializeJSON(); // returns => {'input': 'true'}

In the eyes of standard W3C rules, it is like if only checked inputs exist, and the others are ignored. This would be equivalent:

<form class="form-1">
</form>
<form class="form-2">
    <input type="radio" name="input" value="true" checked>
</form>

When using the option checkboxUncheckedValue in serializeJSON, the rules are changed so unchecked checkboxes are not ignored anymore, and have a different value when not checked. I'll use the value "FOOBAR" here instead of "false" to make it a little more clear:

$('.form-1').serializeJSON({checkboxUncheckedValue: "FOOBAR"}); // returns => {'input': 'FOOBAR'}
$('.form-2').serializeJSON({checkboxUncheckedValue: "FOOBAR"}); // returns => {'input': 'FOOBAR'}

In the eyes of the checkboxUncheckedValue rule, this would be the equivalent form:

<form class="form-1">
    <input type="radio" name="input" value="FOOBAR" checked>
    <input type="radio" name="input" value="FOOBAR" checked>
</form>
<form class="form-2">
    <input type="radio" name="input" value="true" checked>
    <input type="radio" name="input" value="FOOBAR" checked>
</form>

And if you have multiple inputs with the same name, the last value is used (unless using the array name syntax input[] in which case it would build an array).

What to do

I recommend you simply remove the option checkboxUncheckedValue, because it seems that you want the standard W3C behavior with your form.

Another option would be to change the form to use a single checkbox instead of a radio button

<form class="form-1">
    <input type="checkbox" name="input" value="true" data-unchecked-value="false" />
</form>
<form class="form-2">
    <input type="checkbox" name="input" value="true" data-unchecked-value="false" />
</form>

I hope the plugin is working as expected for you otherwise. Thanks for the detailed bug report!

MBWebTechMariusz commented 2 years ago

I get your explanation, but I'm not sure if the option "checkboxUncheckedValue" should mark all radio-inputs as checked:

From:

<form class="form-1">
    <input type="radio" name="input" value="true">
    <input type="radio" name="input" value="false">
</form>
<form class="form-2">
    <input type="radio" name="input" value="true" checked>
    <input type="radio" name="input" value="false">
</form>

With: .serializeJSON({checkboxUncheckedValue: "FOOBAR"});

To:

<form class="form-1">
    <input type="radio" name="input" value="FOOBAR" **checked**>
    <input type="radio" name="input" value="FOOBAR" **checked**>
</form>
<form class="form-2">
    <input type="radio" name="input" value="true" checked>
    <input type="radio" name="input" value="FOOBAR" **checked**>
</form>

I don't see the reason why the setting "checkboxUncheckedValue" should set the checked-state for radio-inputs, especially when you can't have multiple items checked in radio-inputs (normally).

I would add another option for the desired behaviour, like "radioUncheckedValue" or something, but as you said - in this case, you wouldn't need to set checkboxUncheckedValue at all (and that's what I actually want - that the setting "checkboxUncheckedValue" doesn't change my radio-inputs).

Unfortunately, I can't seperate the form (because it consists of checkboxes, radio-inputs and other inputs), and because of that, the "checkboxUncheckedValue" needs to be set. So I'm stuck with just choosing the 3.1.1 for this. :(