I want to use something similar to the Knockout foreach construct to iterate over the properties of an object. Here is what I am trying to create...
DESIRED RESULT
<table> <tr> <td>Name 1</td> <td>8/5/2012</td> </tr> <tr> <td>Name 2</td> <td>2/8/2013</td> </tr> </table>
However, my model looks like this...
JS
function DataModel(){ this.data = ko.observableArray([{ entityId: 1, props: { name: 'Name 1', lastLogin: '8/5/2012' } }, { entityId: 2, props: { name: 'Name 2', lastLogin: '2/8/2013' } }]); } var dataModel = new DataModel(); ko.applyBindings(dataModel);
Each row has an entityId and props which is an object itself. This template doesn't work, but how would I change it to generate the desired table above?
EDIT: The props
in this example are name
and lastLogin
, but I need a solution that is agnostic to what is contained inside props
.
I have this FIDDLE going as well.
HTML
<div data-bind="template: { name: 'template', data: $data }"></div> <script type="text/html" id="template"> <table> <tr data-bind="foreach: data()"> <td data-bind="text: entityId"></td> </tr> </table> </script>
There are two methods to iterate over an object which are discussed below: Method 1: Using for…in loop: The properties of the object can be iterated over using a for..in loop. This loop is used to iterate over all non-Symbol iterable properties of an object.
Purpose. The foreach binding duplicates a section of markup for each entry in an array, and binds each copy of that markup to the corresponding array item. This is especially useful for rendering lists or tables.
Lodash forEach() to Iterate over Objects Lodash has a helpful iteration methods, such as forEach and map that work on objects as well as arrays. console.
In a modern browser (or with an appropriate polyfill) you can iterate over Object.keys(obj)
(the method returns only own enumerable properties, meaning that there is no need for an additional hasOwnProperty
check):
<table> <tbody data-bind="foreach: {data: data, as: '_data'}"> <tr data-bind="foreach: {data: Object.keys(props), as: '_propkey'}"> <th data-bind="text: _propkey"></th> <td data-bind="text: _data.props[_propkey]"></td> </tr> </tbody> </table>
Fiddled.
NB: I was simply curious to see if this would work, the template body above is more polluted than what I'd like to use in production (or come back to a few months later and be like "wtf").
Custom binding would be a better option, my personal preference though would be to use a computed observable or a writeable computed observable (the latter would be handy when working with json
responses a-la restful api).
You could always create a binding handler to handle the transformation.
ko.bindingHandlers.foreachprop = { transformObject: function (obj) { var properties = []; ko.utils.objectForEach(obj, function (key, value) { properties.push({ key: key, value: value }); }); return properties; }, init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { var properties = ko.pureComputed(function () { var obj = ko.utils.unwrapObservable(valueAccessor()); return ko.bindingHandlers.foreachprop.transformObject(obj); }); ko.applyBindingsToNode(element, { foreach: properties }, bindingContext); return { controlsDescendantBindings: true }; } };
Then apply it:
<div data-bind="template: { name: 'template', data: $data }"></div> <script type="text/html" id="template"> <table> <tbody data-bind="foreach: data"> <tr data-bind="foreachprop: props"> <td data-bind="text: value"></td> </tr> </tbody> </table> </script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With