Creating a slightly different 'checked' binding handler for radio buttons

128 views
Skip to first unread message

gareth.b...@gmail.com

unread,
Apr 23, 2014, 2:39:06 AM4/23/14
to knock...@googlegroups.com
I'm looking to create a variant of the 'checked' binding handler, for a specific case in our app. I'm still puzzling over how to approach editing a version based off Knockout's 'checked' binding...

Ultimately, I need to have the radio button set a value to true or false, depending on if it's selected. The existing implementation is set to only update on the 'click' event - it doesn't fire an event for the deselection. (Which makes sense, for all other usages)

I've created a jsFiddle showing my issue in the 2nd example: http://jsfiddle.net/overflew/5awqq/

So in the app/use-case:
- I have a variable list of items, of which only one can be selected (managed by radio buttons)
- The viewmodel for each row contains a bool property - e.g.: 'isSelected'

Looking through the source for the checkbox on GitHub ( https://github.com/knockout/knockout/blob/master/src/binding/defaultBindings/checked.js#L16 ), I'm not quite sure which part to pinpoint:

This looks like it's defending against the 'deselection' event - though it never gets fired anyway..

function updateModel() {
[...]
  // We can ignore unchecked radio buttons, because some other radio
    // button will be getting checked, and that one can take care of updating state.
    if (isRadio && !isChecked) {
        return;
    }

Here I'm wondering if I need to change the event handler registration to something broader than 'click', though I'm not yet sure.

function updateView() {
[...]
ko.utils.registerEventHandler(element, "click", updateModel);

Any input on how to approach this style of modified checked binding / radio button usage would be appreciated.

Bart Breen

unread,
Apr 24, 2014, 3:39:57 AM4/24/14
to knock...@googlegroups.com, gareth.b...@gmail.com
Hi Gareth,
I would suggest going with the traditional way (i.e. without a custom binding handler) and instead add a listener to the observable which will handle the deselection.
http://knockoutjs.com/documentation/observables.html (look under "Explicitly subscribing to observables" for the "beforeChange" listener, but you could do it post change anyway)

Bart Breen

unread,
Apr 25, 2014, 1:09:35 AM4/25/14
to knock...@googlegroups.com, gareth.b...@gmail.com
I have updated the fiddle with what i suggested: http://jsfiddle.net/5awqq/3/ 
Let me know whether this is appropriate or not.

gareth.b...@gmail.com

unread,
May 9, 2014, 9:21:33 PM5/9/14
to knock...@googlegroups.com, gareth.b...@gmail.com
Thanks Bart. Good solution - though one constraint I have is that I don't (/won't) have a reliable identifier for each item. (Sorry - I didn't really include it clearly first time around)

In short - I have:
  • Some records loaded from the DB with IDs. The text is editable.
  • The ability to add new editable records (so no DB ID yet)
  • A 3rd area, where another record can be set to 'true'

In playing with this, I'm looking to either:

1) In the subscription, 
  • Find all other items in the same group ( $(":radio[name=groupName]") ), 
  • Get the ko object ( ko.dataFor(element) ), 
  • Ensure the property is set to false.

2) In the 'boolChecked' KO binding handler, monitor the blur event and set the value to false as it's deselected
The issue I'm having with this is that when they click elsewhere on the page, it's proving difficult to stop it deselecting that item. (And when attaching the Chrome dev tools, breaking on blur() event seems to stop .change() handler, making things a bit difficult...)


I'll update once I have a stable example of either. (Sorry for the delay in replying)

Michael Best

unread,
May 10, 2014, 4:49:32 AM5/10/14
to knock...@googlegroups.com
I've answered your question on SO. Let me know how it goes.

-- Michael

gareth.b...@gmail.com

unread,
May 24, 2014, 9:07:12 PM5/24/14
to knock...@googlegroups.com
Thanks Michael. Very clean solution, and I quite like your solution to managing the groups.

For anyone finding this thread - the Stack Overflow question is here, which contains some more detail about the implementation.

One last thing I trying to work through - For the main item, I have a 'none' selection, which introduces an item being false. 

I've taken Michael's solution and updated it to use 'checkedValue' property (only ever true/false), rather than correlating directly to element.checked. 

I'm currently stuck, trying to cover all selection cases. Namely:
- Some updates to the model or clicking require 2 clicks (as the 'update guarding' logic trips it up...)
- Updates to the model
- Deselection from the main item

My in-progress jsFiddle:

Still trying to get my head around having radiobuttons bound to both true + false, and thinking through how to get the 'false' item on load to only be selected when something else isn't...

Any help appreciated.

Michael Best

unread,
May 26, 2014, 5:23:38 PM5/26/14
to knock...@googlegroups.com
For your none/everything radio buttons, which switch a single observable between true and false, you can just use the normal checked binding, which handles this case just fine: http://jsfiddle.net/g25MX/1/

-- Michael

gareth.b...@gmail.com

unread,
May 27, 2014, 7:32:23 AM5/27/14
to knock...@googlegroups.com
Good, but it then loses the 'single setting' functionality (switching the value between the list & the all/none observable doesn't update the deselected one.)

In the morning, I'll try either:
- Make the 'none' (false) item a special case - normal 'checked' binding + click handler that knows to set the other values to false
- Consider having the binding handler remember the other values.

Will see how I go. Hopefully that first option pans out...
Reply all
Reply to author
Forward
0 new messages