macek/jquery-serialize-object

[request] Include unchecked checkbox in result

ikhsan017 opened this issue · 13 comments

Hi @macek ,

It is possible to add feature to include unchecked checkbox into the object or json result, something like

{
    "some_unchecked_checkbox" : false
}

regards
Ikhsan

@ikhsan017 this is unlikely to be implemented as this project is moving forward to meet the specs described for W3C HTML JSON form submission

Unchecked checkboxes are not meant to be submitted to the server, but this is not really a big problem.

Think of it this way:

<!-- client side -->
<form>
  Email: <input name="email" value="helloworld@example.com">
  <input type="checkbox" name="newsletter">Receive News Letter?
</form>
<script>
  var xhr = $post("/update-profile", $("form").serializeObject());
  // ...
</script>

Then in /update-profile you can get both true and false values for the checkbox quite easily

// server side
app.post("/update-profile", function() {
  var email = req.param("email");
  var newsletter = Boolean(req.param("newsletter"));
  // ...
});

If the newsletter value isn't present in the form submission, it's implicitly false and the app can easily detect that.


In case this example is a little confusing, let's look at it one more way.

req.param("newsletter") can be set to one of two values.

  • if the <input> is checked, the value will be set to "on".
  • if the <input> is unchecked, it will not be submitted, and when we check for the value, it will be unset or null.

By simply casting the value as a Boolean, we get both states

Boolean("on"); // true
Boolean(null); // false

We now have a perfectly toggling checkbox without submitting the unchecked checkbox to the server


Because this is such a popular request, I'm tempted to make it an option for this plugin. However, larger organizations such as W3C and jQuery are leaning on one another here.

W3C says "don't submit unchecked checkboxes" and jQuery is saying "we use the standard W3C rules".

This plugin depends on the output of jQuery.serializeArray so the scope of this plugin doesn't even know that unchecked checkboxes exist!

This is not the first time .serializeArray has come up in the issues of this plugin. serializeArray doesn't give us any information on the type of value (i.e., text, number, boolean, etc); everything is just treated as text. We already have to remove it as a dependency if we're going to meet the W3C JSON form submission spec.

I've already started working on my own serializer that would give this plugin some more flexibility in terms of user options. Things like {serializeWithType: true} or {includeUncheckedInputs: true} could be a possibility.

I'll leave this open to see what other people have to say about it. I suspect most people are requesting the "submit unchecked checkboxes" because they don't realize that there's still a way to detect the value as true / false on the server side. If this is the case, we want to educate people rather than implement new features to help people do things the "wrong" way.

Feel free to reply in this thread if you have any other questions or ideas ✌️

Yeah, I got it, as implementation on major browser also have same behavior. But in my case, I am saving the form data as json-encoded data and put it into some textarea and need a clear state of the checkbox.

scanning it like using $('[type=checkbox]:not(:checked)') should be fine, but put it in the object will little bit harder when it has name like input[child][grandchild][etc] since I am not so fluent with regex matching :D

Create an optional parameter to parse unchecked checkbox should be fine too, and follow the standard by default.

btw, I found it already implemented here https://github.com/marioizquierdo/jquery.serializeJSON , quite similar project to yours :)

regards
Ikhsan

@ikhsan017 yup I'm aware of his project, but I disagree with some of the implementation details. Also, his checklist of how his project is "better" maybe have been true at the time he wrote it, but that's no longer the case.

I'll keep you updated here with the any related progress.

@macek Just like to add my use case to this discussion .

We call serializeObject() on the form and then merge it into another object. So not have the false value for some properties is causing us headaches (doesn't update the other object because the property doesn't exist).

Agreed it shouldn't be enabled by default but for the cases where you are using the object client side (rather then just submitting the form to the server), its really needed to get a complete representation of the object (we now have to go an manually insert the false value)

Hope this makes sense.

Ended up adding a extra method onto the FormSerializer to support this because trying to work around it ended up being a nightmare.

FormSerializer.prototype.serializeObjectIncludingUnchecked = function() {

    if (this.length > 1) {
        return new Error("jquery-serialize-object can only serialize one form at a time");
    }

    var formSerializer = new FormSerializer($, this);

    var pairs = this.serializeArray();

    //Add in unchecked checkbox values.
    var results = this.find(':checkbox:not(:checked)').clone().prop('checked', true).serializeArray();

    for (var i = 0; i < results.length; i++) {
        results[i].value = results[i].value === "true" ? false : null; //could support an attribute on the input to determine unchecked value, but this should be good enough for now. 
    }

    pairs = pairs.concat(results);

    return formSerializer.addPairs(pairs).serialize();
};
macek commented

@markkemper1 you might want to make sure you're modifying the latest version 2.5.0

2.5.0 will allow .serializeObject to be called on multiple elements so you could do

var $uncheckedInputs = $('#my-form :checkbox:not(:checked)').clone().prop('checked', true);
var obj = $('#my-form :input').add($uncheckedInputs).serializeObject();

Sorry for the delay on adding this feature to the latest version. Hope this helps a little better.

Not sure that will help as I need to set the value to false in the resulting js object. In your example the properties would end up with true (or the value of the value attribute) ?.

I only set the value to true, so that serializeArray will include the key / value pairs.

The code does the following:

  • Gets the unchecked boxes, creates a copy, sets the checked attribute (so they will be serialized) and serializes them into an array
  • Then goes through each item in the array and sets (back) all the values back to false
  • Then add these resulting pairs to the normal function of the library.
macek commented

@markkemper1, My bad, I kinda just glanced at your code and threw and example together. The idea in principle would still work though: add additional fields - whatever they might be - to be serialized along with your other form fields.

Maybe this will help lead you to a more complete solution

function mySerializeFunction($form) {
  var $uncheckedInputs = $form.find(':checkbox:not(:checked)').clone().map(function() {
    return $(this).prop('checked', true).val(false);
  });
  return $form.find(':input').add($uncheckedInputs).serializeObject();
}

var obj = mySerializeFunction($('#my-form'));

Again, it's not tested, but you can construct the additional fields however you want. A modification of the core plugin should not be necessary.

Thanks, that looks better but (just glancing the source) won't the value be
a string "false" instead of the literal false?

On 16 July 2015 at 14:40, Paul Maček notifications@github.com wrote:

My bad, I kinda just glanced at your code and threw and example together.
The idea in principle would still work though: add additional fields -
whatever they might be - to be serialized along with your other form fields.

Maybe this will help lead you to a more complete solution

function mySerializeFunction($form) {
var $uncheckedInputs = $form.find(':checkbox:not(:checked)').clone().map(function() {
return $(this).prop('checked', true).val(false);
});
return $form.find(':input').add($uncheckedInputs).serializeObject();
}
var obj = mySerializeFunction($('#my-form'));

Again, it's not tested, but you can construct the additional fields
however you want. A modification of the core plugin should not be necessary.


Reply to this email directly or view it on GitHub
#33 (comment)
.

macek commented

@markkemper1 version 2.4.0 received support for serializing booleans, but only for type="checkbox" inputs.

Serializing the other types (like type="number") are targeted for 3.x - which I haven't had a ton of time to work on lately.


Edit

Looking at it closer, you're right, the value would be the string value "false" because of the hacked way Boolean encoding works in 2.x

It looks like the checkboxes have to be children of the selector (variable $form in this scope). This is just another nasty side effect of jQuery's serializeArray function. Add another tally...

Anyway, if you must get the boolean value of your unchecked checkboxes, I guess your other solution might be the only way for right now. Sorry if I've wasted any of your time.

dandv commented

@marioizquierdo, any comments on #33 (comment) ?

It's true I wrote the "why it's better" a while ago and certain checks don't really differentiate serializeJSON anymore. I just updated the README file with more accurate info in this regard.

Also, implementing unchecked checkboxes in jquery.serializeJSON was not easy. It complicated the interface, the implementation and the available use cases, but there was hight demand for it.

Relying on serializeArray is good overall and ensures a perfect standard behavior, but it won't include non-standard elements like unchecked checkboxes. Blame HTML for that ;)

rhyzx commented

Solution:

const $target = $(target)
const data = new FormSerializer($, $target)
.addPairs($target.serializeArray())
// add unchecked value
.addPairs(
  $target.find(':checkbox[name]:not(:checked)')
  .toArray()
  .map(checkbox => ({name: checkbox.name, value: false}))
)
.serialize()