Vue: Checkbox doesn't check with binding checked attribute

Created on 11 May 2017  Â·  17Comments  Â·  Source: vuejs/vue

Version

2.3.3

Reproduction link

http://jsfiddle.net/yMv7y/2617/

Steps to reproduce

Instead of using v-model on the native checkbox, simply use :checked="computedProp" to manage the checkbox's checked state and block UI-checking (mouse-click, and tab-space-ing) by doing e.preventDefault() on click.

What is expected?

For the checkbox to check.

What is actually happening?

The checkbox is not checking, despite that the computed property is evaluating to true.


I don't want to use v-model because, from what I understand, v-model is short for something like :checked="foo" and @change="val => { foo = val }" (the exact logic found here), and since I am making a minimal Checkbox component only to wrap the native checkbox with some elaborate CSS, I'd like to refrain from having to maintain any "data" props, and instead of the event handler v-model comes with, I'd like to simply hoist the event up.

Most helpful comment

Looks like the behavior is caused by the e.preventDefault() call. My assumption is that this is because Vue's DOM update kicks in via a microtask before the browser checks for preventDefault behavior - the browser resets the checkbox state when it sees that the event default behavior has been prevented, overwriting the DOM changes made by Vue.

In Firefox this works properly, so I think this is a Chrome/Webkit-only behavior.

I'm not sure why you need e.preventDefault() here, but if you really need it, you can wrap the $emit calls in a setTimeout to get around this: http://jsfiddle.net/yMv7y/2621/

All 17 comments

You never set this.checked to a value. So the checkbox won't update. Besides, do yourself a favor and use v-model. It's much easier, even for wrapped components. That doesn't stop the component from implementing a v-model interface itself or customizing the model.

This is an implementation that works:
http://jsfiddle.net/t08v9ny6/3/

Thanks for looking into this @Nirazul

this.checked is a prop, so it doesn't need to be declared but rather passed in (which I do in the Jsfiddle). The input element's checked attribute is evaluated with the expression this.checked === this.value where this is the vm --the computed property isChecked in the Jsfiddle-- so it should update.

As I've said, the reason why I would prefer to avoid v-model is so that the component doesn't need to maintain a state -- which you do in your example by using data(). In your example, if a variable is bound to the prop checked, this.val doesn't update when the parent mutates the value because this.val is a new property created per instance, reflecting the state of the component.

Functionally, what I need can be achieved by using computed property which bubbles the change event up on set and returns the prop value on get. However, this is not the point I'm making. I'm addressing what _seems_ to be a Vue bug with the input(type=checkbox) element so it could be further investigated.

Then value is never defined. Because you're setting the prop for the v-model binding to checked.

But in any case, there's a subtle difference between HTML attributes and props. But I'm not skilled enough to explain, why the checked binding wouldn't work. I'll leave this to the library authors ¯_(ツ)_/¯

@Nirazul

value is also a prop; it's being passed in here (it's in the HTML slot): <o-checkbox v-model="testVal" :value="true">Label</o-checkbox>

Aw don't sell your self short! Thanks for looking into it nonetheless.

Looks like the behavior is caused by the e.preventDefault() call. My assumption is that this is because Vue's DOM update kicks in via a microtask before the browser checks for preventDefault behavior - the browser resets the checkbox state when it sees that the event default behavior has been prevented, overwriting the DOM changes made by Vue.

In Firefox this works properly, so I think this is a Chrome/Webkit-only behavior.

I'm not sure why you need e.preventDefault() here, but if you really need it, you can wrap the $emit calls in a setTimeout to get around this: http://jsfiddle.net/yMv7y/2621/

@yyx990803

Thanks for looking into it! That's awesome -- I didn't think to see if it was a browser issue.
I use e.preventDefault() to block the checkbox from checking _just because it was clicked_ and also to ensure that the single source of truth of what the checkbox is reflecting is within the View model rather than the DOM; I needed the checked state of the input to strictly reflect the data of the View.

@yyx990803 - thank you for that example! I have had a lot of trouble with this in Vuex - e.preventDefault() throws the whole thing out of whack.

What I was going for was only actually toggling the checkbox if a Vuex action (and mutation) were successful. So basically, even if a user checked a checkbox - that doesn't mean I want it to go to a checked state - I just want it to keep following its computed property it was already set to using :checked. And because using v-model in Vuex isn't a good idea, I found that the UI was getting toggled even though I hadn't committed a mutation. I found this strange. In further research, I actually found there wasn't a good answer to this question. I couldn't believe it - as I believed this to be a common pattern - not wanting a checkbox to toggle until some action is complete - that way you can deal with an error and not end up toggling the checkbox - say, for like a user's preferences page.

Nothing worked until doing something like your setTimeout example. Thank you, and thank you @hirokiosame for asking this question.

<input type="checkbox" :checked="checked" :data-wug="checked"/> renders to <input type="checkbox" data-wug="true"/> and not <input type="checkbox" checked data-wug="true"/> as expected.

@GufNZ <input type="checkbox" :checked="checked" :data-wug="checked"/> is trying to call a function named checked(), that's why it doesn't render anything. If you want it to output a string checked, use single quotes:

<input type="checkbox" :checked="'checked'" :data-wug="checked"/>

@dsignr See above; checked is a source of a boolean value, in this case true so the checkbox should still have that attribute set, and does not. Or at least, did not, at the time of writing - I haven't checked this in a while.

" 'checked' " => " checked "

For what it's worth:

      <input 
        type="checkbox" 
        :checked="justTesting"
      >

Does work with a justTesting computed value giving 'true'.

    justTesting(){
      return true
    }

In general computers do mess things up when things are named the same. In this case the attribute 'checked' and the computed value 'checked'.

[still looking for a clear example on using checkboxes in combination with vuex]

Same happens on <input type="radio" /> elements.

This is very confusing, how do we use binding on a checkbox? v-model doesn't work on the input element from within a child components for some reason??

This is still an issue for me, I still don't see why doing something along the lines of <input type="checkbox" :checked="someBooleanValue"> wouldn't work properly? I don't want to be using v-model since I also want the boolean toggle to occur programatically when clicking on the parent element of the input element.

Instead of setting input field checked property to "checked". we have to push value in the model. This will automatically mark the corresponding checkbox as checked.

HTML

<li class="list-group-item" v-for="home in home_data">
    <input  v-model="homes" :value="home.id" type="checkbox" @click="home.visible=!home.visible"  /> {{home.name}} 
</li>

Data

"home": [ { "id": 5, "staff_id": 13, "home_id": 8, "created_at": "2020-09-13T13:24:52.000000Z", "updated_at": "2020-09-13T13:24:52.000000Z" }, { "id": 6, "staff_id": 13, "home_id": 6, "created_at": "2020-09-13T13:24:52.000000Z", "updated_at": "2020-09-13T13:24:52.000000Z" } ] }

Function where we are putting data into our model.

this.form = this.edit_data = row;
                row.home.forEach((item, index)=> {
                       console.log(item);
                       this.assigned_home.push(item.home_id);
                     this.homes.push(item.home_id);
                   });

Was this page helpful?
0 / 5 - 0 ratings

Related issues

robertleeplummerjr picture robertleeplummerjr  Â·  3Comments

franciscolourenco picture franciscolourenco  Â·  3Comments

bdedardel picture bdedardel  Â·  3Comments

gkiely picture gkiely  Â·  3Comments

paceband picture paceband  Â·  3Comments