I have a nested JSON like this:
[
{
"Run 1": {
"1.2.0": {
"Server": {
"TestSuite 1": [
{
"version": "1.2.0",
"type": "server",
"testdef": "TestSuite 1",
"testcaseid": "TestCase 1",
"status": "pass"
},
{
"version": "1.2.0",
"type": "server",
"testdef": "TestSuite 1",
"testcaseid": "TestCase 2",
"status": "fail"
}
],
"TestSuite 2": [
{
"version": "1.2.0",
"type": "server",
"testdef": "TestSuite 2",
"testcaseid": "TestCase 1",
"status": "pass"
}
]
}
}
}
}
]
I want to display it on the html page as an unordered list like this:
but I am seeing the last item repeated multiple times:
This is html with knockout.js bindings:
<div class="row">
<div class="tree">
<ul data-bind="foreach: {data: testResults, as: 'item'}">
<li data-bind="foreach: {data: Object.keys(item), as: 'key'}"><span data-bind="text: 'Run' + key"></span>
<ul data-bind="foreach: {data: item[key], as: 'item2'}" class="child">
<li data-bind="foreach: {data: Object.keys(item2), as: 'key2'}"><span data-bind="text: key2"> </span>
<ul data-bind="foreach: {data: item2[key2], as: 'item3'}" class="child">
<li data-bind="foreach: {data: Object.keys(item3), as: 'key3'}"><span data-bind="text: key3"> </span>
<ul data-bind="foreach: {data: item3[key3], as: 'item4'}" class="child">
<li data-bind="foreach: {data: Object.keys(item4), as: 'key4'}"><span data-bind="text: key4"> </span>
<ul data-bind="foreach: {data: item4[key4], as: 'item5'}" class="child">
<li data-bind="foreach: {data: Object.keys(item5), as: 'key5'}"><span data-bind="text: item5.testcaseid, css : {'bg-success' : item5.status == 'pass', 'bg-danger' : item5.status == 'fail'}"> </span><br></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
testResults
is a ko.observableArray() in my view model which contains the above JSON.
What would be the correct way to display the leaf elements only once
The problem is you're nesting so many foreach
bindings that you're getting lost in how many nested loops there are. Your last look at the very end is not needed. You loop through the keys of the test suite objects when you don't need to. Remove that last foreach binding.
<ul data-bind="foreach: {data: item4[key4], as: 'item5'}" class="child">
<li>
<span data-bind="text: item5.testcaseid,
css : {'bg-success' : item5.status == 'pass',
'bg-danger' : item5.status == 'fail'}">
</span><br>
</li>
</ul>
Don't do this. Do you really want to have to look at that view and try to maintain that?
I have two suggestions, either map the results out to arrays and foreach over those or iterate over the properties alone with the help of custom bindings to make it more manageable. You can use this foreachprop
binding handler to do this:
<ul data-bind="foreach: testResults">
<li data-bind="foreachprop: $data"><span data-bind="text: key"></span>
<ul data-bind="foreachprop: value">
<li><span data-bind="text: key"></span>
<ul data-bind="foreachprop: value">
<li><span data-bind="text: key"></span>
<ul data-bind="foreachprop: value">
<li><span data-bind="text: key"></span>
<ul data-bind="foreach: value">
<li><span data-bind="text: testcaseid"></span></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>