Knockout JS Checkbox binding not updating correctly

4.5k Views Asked by At

I have a checkbox that is bound to a property on my object used in my observableArray. I can't get the checkboxes to update correctly when clicked. The value doesn't set correctly nor does the checkbox uncheck itself when i click it. The example bindings have a property on the ViewModel directly rather then a property of an object used inside of the ViewModel. Can anyone shed light on how to get this working?

            <tbody id="StatusGrid" data-bind="foreach:{data: Statuses, as: 'status'}">
            <tr data-bind="attr: { index: $index }" style="padding-bottom:5px;">
                <td style="padding-bottom:5px;">
                    <input class="statusID" data-bind="value: status.StatusID, visible: status.ShowID, attr: { name: 'Statuses[' + $index() + '].StatusID'}" />
                </td>
                <td style="padding-bottom:5px;">
                    <input class="description" data-bind="value: status.Description, attr: { name: 'Statuses[' + $index() + '].Description'}" />
                </td>
                <td style="padding-bottom:5px;">
                    <input type="checkbox" class="active" data-bind="value: status.Active, checked: status.Active, click: $parent.updateCheckbox, attr: { name: 'Statuses[' + $index() + '].Active'}" />
                </td>
                <td style="padding-bottom:5px;">
                    <input type="button" data-bind="click: $parent.removeStatus, visible: status.IsNew" value="Remove" />
                </td>
            </tr>
        </tbody>


 //////KNOCKOUT//////
var _viewModel = new ViewModel();

function status(index) {
    this.StatusID = ko.observable('');
    this.Description = ko.observable('');
    this.Index = ko.observable(index);
    this.Active = ko.observable(true);
    this.ShowID = ko.observable(false);
    this.IsNew = ko.observable(true);
    return this;
};

function ViewModel() {
    var self = this;

    self.Statuses = ko.observableArray(convertJSONToKoObservableObject());
    //self.Statuses.push(new status(0));

    //Ko Functions
    self.addStatus = function () {
        var index = $('#StatusGrid tr:last').attr('index');
        self.Statuses.push(new status(index + 1));
    }

    self.removeStatus = function (row) {
        self.Statuses.remove(row);
    }

    self.updateCheckbox = function (row) {
        var index = row.Index();
        var checkbox = $('#StatusGrid tr').eq(index).find('[type=checkbox]');
        if ($(checkbox).is(":checked")) {
            $(checkbox).attr('checked', false);
            row.Index(false);
        }
        else {
            $(checkbox).attr('checked', true);
            row.index(true);
        }
        return true;
    }
}

//FUNCTION
function convertJSONToKoObservableObject() {
    var json = JSON.parse('@Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model.Statuses))');
    var ret = [];
    var index = 0;
    $.each(json, function (i, obj) {
        var newOBJ = new status(index);
        newOBJ.StatusID = ko.observable(obj["StatusID"]);
        newOBJ.Description = ko.observable(obj["Description"]);
        newOBJ.Active = ko.observable(obj["Active"]);
        newOBJ.Index = ko.observable(index);
        newOBJ.ShowID = ko.observable(false);
        newOBJ.IsNew = ko.observable(false);
        ret.push(newOBJ);
        index++;
    });

    return ret;
}

//BIND UP!
ko.applyBindings(_viewModel);
3

There are 3 best solutions below

1
omerio On

I think may be the value binding is interfering with the checked binding

<input type="checkbox" class="active" data-bind="value: status.Active, checked: status.Active, click: $parent.updateCheckbox, attr: { name: 'Statuses[' + $index() + '].Active'}" />

Try it with the checked binding only I believe knockout keeps the observable in sync

<input type="checkbox" class="active" data-bind="checked: status.Active, click: $parent.updateCheckbox, attr: { name: 'Statuses[' + $index() + '].Active'}" />

If I understand your code correctly, I also doubt you need to check/uncheck the checkbox using jQuery, knockout should handle the checking and updating the observable automatically by using the checked binding

0
user1732364 On

After making the change suggested and then tweaking the html further for the mvc post. Below works for the check binding and the value binding for the post back :)

                        <input type="checkbox" class="active" data-bind="checked: status.Active" />
                    <input type="hidden" data-bind="value: status.Active, attr: { name: 'Statuses[' + $index() + '].Active'}">
0
Andrew Sin On

You can use an if binding, so:

<td style="padding-bottom:5px;">
<span data-bind="if: status.Active"> 
   <input type="checkbox" data-bind="value: status.Active" checked/>
</span>
<span data-bind="ifnot: status.Active"> 
   <input type="checkbox" data-bind="value: status.Active" />
</span>
</td>

It may not be pretty, but it works instead of using 2x 2-way binding together (namely, value and checked binding together), which does not seem to give you your typical checkbox behaviour. ...