Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using checkedValue binding with radio buttons

I'm trying to make use of the checkedValue binding introduced in knockout version 3, with radio buttons , but am not getting the behavior I expect.

Here's an example: (the viewModel has two properties; list is an array; checkedVal is an observable)

<div data-bind="foreach:list">
    <input type="radio" data-bind="
        checkedValue: {
            data: $data,
            index: $index(),
        },
        checked: $parent.checkedVal
    "/>
    <span data-bind="text: $data"></span>
</div>

JSFiddle

I expect the radio buttons to behave normally, and checkedVal to be an object containing the data and index. checkedVal is as I expect, but the radio buttons don't select. Oddly, in my actual code the behavior is inconsistent; sometimes the radio butttons work and sometimes they don't, but it consistently doesn't work in the fiddle, as far as I can see.

Is this a bug, or am I misunderstanding how this should be working?

like image 632
Retsam Avatar asked Nov 10 '22 16:11

Retsam


1 Answers

Your checkedValue binding becomes a function as follows:

function () {
    return {
        data: $data,
        index: $index(),
    };
}

Each time the checked binding updates, it calls this function to get the value. But the function always returns a new object. Even though the objects contains the same data, Knockout doesn't see them as the same.

You can solve this by making the value a string.

    <input type="radio" data-bind="
        checkedValue: JSON.stringify({
            data: $data,
            index: $index(),
        }),
        checked: $parent.checkedVal
    "/>

Or by binding to a consistent value.

    <input type="radio" data-bind="
        checkedValue: $data,
        checked: $parent.checkedVal
    "/>

EDIT:

You can use a custom binding that follows the same pattern as checked, but allows for a comparison function.

ko.bindingHandlers.radioChecked = {
    init: function (element, valueAccessor, allBindings) {
        ko.utils.registerEventHandler(element, "click", function() {
            if (element.checked) {
                var observable = valueAccessor(), 
                    checkedValue =  allBindings.get('checkedValue');
                observable(checkedValue);
            }
        });
    },
    update: function (element, valueAccessor, allBindings) {
        var modelValue = valueAccessor()(), 
            checkedValue =  allBindings.get('checkedValue'), 
            comparer = allBindings.get('checkedComparer');
        element.checked = comparer(modelValue, checkedValue);
    }
};

Then objects can be compared by contents.

this.itemCompare = function(a, b) {
    return JSON.stringify(a) == JSON.stringify(b);
}

jsFiddle: http://jsfiddle.net/mbest/Q4LSQ/

like image 130
Michael Best Avatar answered Nov 15 '22 11:11

Michael Best