Knockout + Radiobuttons - getting the value to toggle back to 'false' on uncheck

I have a Knockout app, where I have multiple boolean properties and only 1 can be true. (Rather than 1 underlying property, with each radiobutton being a distinct value).

When I select a radio button, the observable will correctly update to the radio button's checkedValue. When I de-select it, the value remains. Is there any way I can have this set the observable to false?

My JS fiddle example: http://jsfiddle.net/9mu8e/

I'm a little wary of setting an intermediate value, and .subscribe() to sort out all values. (And the scattered booleans is a feature of the REST interface we're sending to).

Update: I've added the 'Add new' button to show the list as being dynamic, and possibly containing duplicate values (so tracking against the text property, or a theoretical DB ID isn't possible). jsFiddle link is updated.

My example:

View:

<div>Current item: 
    <input type="radio" name="group" data-bind="checked: isMostTerrible, checkedValue: true" />
    <span data-bind="text: isMostTerrible"></span>
</div>

<ul data-bind="foreach: itemsList">
    <li>
        <span data-bind="text: name"></span>
        <input type="radio" name="group" data-bind="checked: isMostTerrible, checkedValue: true" />
        <span data-bind="text: isMostTerrible"></span>
    </li>
</ul>

<button data-bind="click: onAddNewItem">Add new</button>

View Model:

var item = function(name, dbId) {


var self = this;

    self.dbId = ko.observable(dbId);
    self.name = ko.observable(name);
    self.isMostTerrible = ko.observable(false);
}

var itemList = [
    new item("Taxes", 1),
    new item("Death", 2)
]

var viewModel = function() {
  var self = this;

    self.isMostTerrible = ko.observable(true);
    self.itemsList = ko.observableArray(itemList);

    self.onAddNewItem = function() {
        // Note - Adding a new item doesn't add a DB ID.
        // (Theoretically occurs on save)
        self.itemsList.push(new item("New item"));
    }
};

ko.applyBindings(viewModel);

Answers


One problem is that Knockout's checked binding simply doesn't work the way you're trying to use it. It's meant to be set up with a single observable that is bound to all radio buttons in the group, with each button having a unique value.

Another problem is that the browser doesn't provide a notification when a radio button is de-selected. (See OnChange event handler for radio button (INPUT type="radio") doesn't work as one value). So a simple binding that listens for the change event won't work.

Still, what you want is possible using a custom binding. The one I've included below uses an observable to keep track of the currently selected radio button so that it can update the model values accordingly.

ko.bindingHandlers.radioChecked = (function() {
    var groupObservables = {};
    return {
        init: function(element, valueAccessor) {
            var name = element.name,
                groupObservable = groupObservables[name] || (groupObservables[name] = ko.observable());

            ko.utils.registerEventHandler(element, 'click', function() {
                valueAccessor()(element.checked);
            });
            var s1 = ko.computed(function() {
                if (valueAccessor()()) {
                    element.checked = true;
                    groupObservable(element);
                }
            });
            var s2 = groupObservable.subscribe(function() {
                valueAccessor()(element.checked);
            });
            ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                s1.dispose();
                s2.dispose();
            });
        }
    };
})();

Fiddle: http://jsfiddle.net/mbest/3dy3s/


ViewModel:-

var item = function (name) {
  var self = this;
  self.name = ko.observable(name);
}
var itemList = [
    new item("Taxes"),
    new item("Death")]

var viewModel = function () {
  var self = this;
  self.isMostTerrible = ko.observable(true);
  self.Checked = ko.observable();
  self.itemsList = itemList;
};
ko.applyBindings(viewModel);

View

<div>Current item:
  <input type="radio" name="group" data-bind="checked: isMostTerrible" /> <span data-bind="text: isMostTerrible()==='on'"></span>
</div>
<ul data-bind="foreach: itemsList">
  <li> <span data-bind="text: name"></span>
     <input type="radio" name="group" data-bind="checked: isMostTerrible,value:name" /> <span data-bind="text: isMostTerrible()==name()"></span>
 </li>
</ul>

Fiddle Demo


Need Your Help

mongo javascript --eval is failing

javascript mongodb

This works within the mongo terminal