0

I am trying to make a table with collapsible rows using knockout. Each "parent" row exists at the top level of my data structure, and each "child" row is an element of a member array.

The problem comes when I update the data from the server. The data updates, but I can tell it is replacing my DOM objects because a checkbox in the "parent" row is getting cleared. I have a fiddle that demonstrates this problem, using a timer to update the data:

http://jsfiddle.net/jdlogicman/m2LWk/

I have read that using a nested foreach instead of {{each}} should fix it, but I can't get that to work - please see the commented-out section in the above fiddle.

If any existing question addresses this, I couldn't tell how. Any help is appreciated

Suresh Kamrushi
  • 15,627
  • 13
  • 75
  • 90
LogicMan
  • 51
  • 8
  • In looking at your fiddle, you are completely replacing all the data in the viewModel on every timer update. Is this what you are wanting to do? – photo_tom Oct 10 '11 at 23:31
  • Yes - that's what you do when you poll to update a dataset from the server. Fetch the whole thing and let KO + mapping figure out what changed. – LogicMan Oct 11 '11 at 19:01

1 Answers1

0

You are not storing the value of the checkbox so it does get cleared each time. Does the fiddle below solve your problem?

http://jsfiddle.net/unklefolk/RYqTT/

<table>
    <thead>
        <th class="checkbox_th"/>
        <th class="name_th" align="left">Sample</th>
        <th class="status_th" align="left">Status</th>
    </thead>
    <tbody data-bind="template: {name: 'sampleRowTemplate', foreach: samples}"></tbody>
    <script type="text/x-jquery-tmpl" id="sampleRowTemplate">
        <tr>
            <td><input type="checkbox" name="select_sample" data-bind="checked: is_checked"/></td>
            <td><span data-bind='text: sample_name'/>   The checkbox =<span data-bind="text:is_checked"/></td>
            <td><span data-bind='text: ""'/><td/>
            </tr>
                {{each sample_runs()}}
                <tr>
                <td/>
                <td><span data-bind='text: $value.name'/></td>
                <td><span data-bind='text: $value.status'/></td>
            </tr>
                {{/each}}
                <!--
                <script type="text/x-jquery-tmpl" id="sampleRunTemplate">
                <td/>
                <td>${name}</td>
                <td>${status}</td>
            </script>
                <tr data-bind="template: {name: 'sampleRunTemplate', foreach: sample_runs  }"></tr>
                -->

            </script>
            </table>            


var mapping = {
    'samples': {
        key: function(item) {
            return ko.utils.unwrapObservable(item.id);
        },
        'sample_runs': {
            key: function(item) {
                return ko.utils.unwrapObservable(item.id);
            }
        }
    }
};

var data = {};
data.samples = [{
    id: "s1",
    sample_name: "AR008",
    is_checked: false,    
    sample_runs: [
        {
        id: "rs1",
        name: "run1",
        status: "done"}]}];
var viewModel = ko.mapping.fromJS(data, mapping);
ko.applyBindings(viewModel);

// update the data every 2 seconds
var i = 0;
setInterval(function() {
    var data = {};
    data.samples = [{
        id: "s1",
        sample_name: "AR008" + i,
        sample_runs: [
            {
            id: "rs1",
            name: "run" + i,
            status: "done" + i}]}];
    // is this right? updateFromJS is deprecated
    viewModel = ko.mapping.fromJS(data, viewModel);
    i++;
}, 2000);
Mark Robinson
  • 13,128
  • 13
  • 63
  • 81
  • It gets around the specific symptom I mentioned by storing the check state in the model, but it doesn't answer the original question per se - how do I get it to work without {{each}}? If you remove the "child" rows and just update the parent, the checkbox state is preserved by KO without moving it into the model. So, it should be doable without altering my model to store stuff as a workaround, right? – LogicMan Oct 11 '11 at 19:02
  • Okay - I have reworked my example and it does not use {{each}}. Take a look at: http://jsfiddle.net/unklefolk/f7J4S/2/ – Mark Robinson Oct 12 '11 at 08:27
  • First of all - thanks for spending time on this. Even the author of KO gave up. I edited down your version, removing the databinding for the checkbox, until I found out what did it - you put a table inside of a 3-column-span TD, and used the nested template on that, which worked. I don't know why KO foreach works with TABLE and not TR, but I think it's a bug in KO. Since what you did works, I'm calling this an answer. Thanks! – LogicMan Oct 12 '11 at 18:50