Kevin
Kevin

Reputation: 241

How to loop through the keys of each object in a knockout observableArray and create table dynamically?

I have an observableArray that is populated by dynamic sql data. So the columns returned could be different at any time.

I want to display the SQL results in a HTML table. However, the below is not working.

This is how I want the output to look...

enter image description here

var viewModel = function(data) {
    var self = this;
   
    // variables   
    self.taskRecordOverview = ko.observableArray([
    {
        "Entity": "DEMO",
        "Period": "2017-07-31T00:00:00",
        "Level": "Level 3",
        "Addendum Errors": null,
        "Cash Process": "Created",
        "Corporate Actions": null,
        "Expenses": null
    },
    {
        "Entity": "DEMO",
        "Period": "2017-07-31T00:00:00",
        "Level": "Level 5",
        "Addendum Errors": "Created",
        "Cash Process": "Created",
        "Corporate Actions": "Created",
        "Expenses": "Created"
    },
    {
        "Entity": "SP00",
        "Period": "2017-07-31T00:00:00",
        "Level": "Level 5",
        "Addendum Errors": "Created",
        "Cash Process": "Approved",
        "Corporate Actions": "Created",
        "Expenses": "Created"
    }
]); 
   
};

ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
  <thead>
    <tr>
      <th>??</th>
    </tr>
  </thead>
  <tbody data-bind="foreach: taskRecordOverview">
    <tr>
      <td data-bind="text: $data"></td>
    </tr>
  </tbody>
</table>

https://jsfiddle.net/f79r4h2g/

Upvotes: 1

Views: 1159

Answers (1)

adiga
adiga

Reputation: 35222

Inside your foreach loop for taskRecordOverview, you need to loop through the keys of each object and display the value. You can do so with Object.keys

Working snippet:

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

  self.taskRecordOverview = ko.observableArray([{
      "Entity": "DEMO",
      "Period": "2017-07-31T00:00:00",
      "Level": "Level 3",
      "Addendum Errors": null,
      "Cash Process": "Created",
      "Corporate Actions": null,
      "Expenses": null
    },
    {
      "Entity": "DEMO",
      "Period": "2017-07-31T00:00:00",
      "Level": "Level 5",
      "Addendum Errors": "Created",
      "Cash Process": "Created",
      "Corporate Actions": "Created",
      "Expenses": "Created"
    },
    {
      "Entity": "SP00",
      "Period": "2017-07-31T00:00:00",
      "Level": "Level 5",
      "Addendum Errors": "Created",
      "Cash Process": "Approved",
      "Corporate Actions": "Created",
      "Expenses": "Created"
    }
  ]);
};

ko.applyBindings(new viewModel());
td, th {
  border: 1px solid #dddddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
  <thead>
    <!--Only one object is enough for header-->
    <tr data-bind="foreach:Object.keys(taskRecordOverview()[0]),visible:taskRecordOverview().length > 0">
      <th data-bind="text:$data"></th>
    </tr>
  </thead>
  <tbody data-bind="foreach: taskRecordOverview">
    <!--Get the keys in each object and loop through them-->
    <tr data-bind="foreach: Object.keys($data)">
      <!--Get the "value" for a key-->
      <td data-bind="text: $parent[$data]"></td>
    </tr>
  </tbody>
</table>

Here's a fiddle


If the nested $data and $parent binding contexts are confusing then we can alias foreach items using as to make it more clear:

<table>
  <tbody data-bind="foreach: { data:taskRecordOverview, as: '_task'}">
    <tr data-bind="foreach: { data:Object.keys(_task), as: '_key'}">
      <td data-bind="text: _task[_key]"></td>
    </tr>
  </tbody>
</table>

Here's an updated fiddle

I know this is an old question, but it is an interesting one and the answer will hopefully help someone else in the future :)

Upvotes: 1

Related Questions