Search code examples
knockout.jsko.observablearray

Table not updated when ko.observablearray changes


I'm binding a ko.observablearray to a table. Each object (called a 'group') in the array is bound to a table. Each group holds another (non-knockout) array of codes. Each code is bound to a row. Each code has a boolean, bound to a checkbox.

If none of the booleans are true in a group, I want all the checkboxes for that group to be enabled. If at least one of the booleans are true in a group, I want the false checkboxes for that group to be disabled, and the true checkboxes to be enabled. When the page is loaded, this works as expected; however, when I check or uncheck a box, the enabled/disabled status of the boxes does not change. Looking at it with the debugger, the observablearray is being updated, but the data in the table is not.

Page:

<table data-bind="foreach: {data: Groups}">
    <thead>
        <tr>
            <th colspan="3" data-bind="text: GroupName"></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: {data: Codes}">
        <tr>
            <td data-bind="text: CodeId"></td>
            <td data-bind="text: Desc"></td>
            <td>
                <input type="checkbox" data-bind="checked: Prime, disable: $root.HasPrime($parent) && !Prime" />
            </td>
        </tr>
    </tbody>
</table>

Javascript function that tests if there are any true values in a group:

var HasPrime = function (gr) {
    for (var i = 0; i < gr.Codes.length; i++) {
        if (gr.Codes[i].Prime === true) {
            return true;
        }
    }
    return false;
};

Fiddle: http://jsfiddle.net/NSLPu/2/


Solution

  • If you want two-way binding you'll need to make things observable. In your case, the Prime properties have to be observables:

    var code1 = {
        'CodeId': 123,
            'Desc': 'Code 1',
            'Prime': ko.observable(false) // <---- changed!
    };
    

    This will require some corresponding changes in the rest of your code:

    • Plain JS calls have to invoke Prime as a method to get the current value
    • Bindings that "do stuff" (like negation, as on the input) will also need to invoke the observable to get the value negated

    See this fiddle for an update.