Ross Anthony Ross Anthony - 1 month ago 6
Javascript Question

Checkbox binding of checked state inverted in Chrome

I'm using Ember v2.5.1 and Ember data v2.6.2 and I have a series of nested components to render a hierarchical tree of categories. There is a closure action in the lowest level controller

categories-tree-node
, which calls the
toggleAddCategory
function in the actions of the
categories-select
component and passes through the relevant category object.

It works as expected in Safari, but for some reason the checked state is passed through inverted in Chrome and FF. The strange thing is that the checked state binding of the value itself renders correctly elsewhere in the template when the checkboxes are changed.

I have created a demo here, if you try checking/unchecking the boxes in Chrome/FF vs Safari you should see the issue. Is there a prefered way of handling this type of action binding with checkbox checked states? I have read that using Observers is considered an anti-pattern in Ember 2 and above, also when I tried this it did not work for the child categories.

Answer

Ok so your twiddle shows that the state is unchanged in Chrome but changed in Safari.

This is most likely a bug with jQuery in Safari because Ember.CheckBox sets checked using the jQuery prop method:

change() {
  set(this, 'checked', this.$().prop('checked'));
}

A workaround solution would be to remove the checked binding to the checkbox itself, and just use the passed in closure action to manually toggle the checked state yourself.

Note: You might want to use the onchange event rather than onclick.

components/categories-tree-node.hbs

{{input type="checkbox"
        /* checked=category.checked remove checked binding from here */
        click=(action toggle category)
        id=(concat elementId '-' category.slug)
}}

...

components/categories-select.js

export default Ember.Component.extend({
  // ...

  actions: {

    toggleAddCategory(category) {
      category.toggleProperty('checked'); // toggle the property yourself

      // ...  
    }

  }
});

If you do want to have a variable bound to the checked state as well (in case you want to render the checkboxes initially as checked), you can use a property within components/categories-tree-node that you set after the component renders to whatever the value of category.checked is.