Checkbox Button doesn't update on Ruby on Rails
Created by: mvastola
Hi, So this just took me two hours to track down.
Background
- I'm running into this issue on Ubuntu 18.10 both on Firefox and on Chrome, but it would appear to be browser/OS-independent.
- When creating checkboxes using the helpers provided by Ruby on Rails (upon which many popular form helper gems like
simple_form
rely), RoR makes use of a paradigm that results in checkbox inputs being preceded by a hidden input tag of the same name. Example:
# Let's say that @eula.accepted is "no":
check_box("accepted", { class: 'eula_check' }, "yes", "no")
<input name="eula[accepted]" type="hidden" value="no" />
<input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
The purpose of this is to provide a fallback value that is received in instances where the checkbox is not checked when the forum is submitted.
Normally this works quite well.
The Issue
Long story short, earlier today I went to use the BSv4 "checkbox buttons" feature, per the documentation. In what was -- in retrospect -- a moment of unchecked hubris, I imagined that this would Be Easy™ and Just Work™. (After several hours, I can now happily report I am much more humble.)
When I attempted to implement these buttons, I repeatedly ran into the problem that the checked
property of the input element never changed. I created a reduced test case in JSFiddle which included this HTML:
<div class="btn-group-toggle" data-toggle="buttons">
<label class="btn btn-info">
<input name="checkbox" type="hidden" value="0">
<input id="cb" name="checkbox" type="checkbox" value="1" autocomplete="off"> Click Me
</label>
</div>
In the end, the problem appears to arise from a block of code in js/src/button.js
:
https://github.com/twbs/bootstrap/blob/c70b043a76764b842c732417fb32325ae2d1ac56/js/src/button.js#L70-L94
Particularly, at issue are these two lines of code:
const input = this._element.querySelector(Selector.INPUT)
// ... snip ...
input.checked = !this._element.classList.contains(ClassName.ACTIVE)
Essentially, the <label>
tag, (to which the required classes have been added to make it appear/act like a button) expects to have it's <input type="checkbox" />
as a child. In the bootstrap source, it is identified by locating the first child element of the <label>
with an <input>
tag. This element then has its checked
attribute flipped.
This should be a simple fix, but either the query selector needs to be more specific by either limiting itself to checkboxes and radios or excluding hidden inputs.
I will probably submit a PA shortly that should achieve this, but I wanted to create an issue as well.
Note: I'm aware it would be possible to simply specify the hidden fields outside the <label>
tag, but Rails puts them together, so this would need to be done manually, and there is really no need for bootstrap to ever modify hidden fields here since radios/checkboxes used this way are already hidden for all intents and purposes.