Reputation: 3428
I am working on a web application
using Asp.Net Mvc
in which I used knockout Js to retrieve and send data to Html View by performing some operation on the data.
For example I have the following data in my array named datainput
Grade Type Volume Price
A Buy 1000 10
A Sell 1200 10
B Sell 100 100
I am computing an array which such that when displaying on a html page it just takes one distinct value of Grade and groups the data under that grade like
A
Buy 1000 10
Sell 1200 10
B
Sell 100 100
Used the above function as follows
self.uniqueSelect = ko.dependentObservable(function () {
var Transactiontypes = ko.utils.arrayMap(self.datainput(), function (item) { return item.Grade })
return ko.utils.arrayGetDistinctValues(Transactiontypes);
});
I dont think there is an issue with above script, not sure though but I went ahead and used the data obtained from the above script and tried to populate it to html as follows
<!--ko foreach: uniqueSelect-->
<tr>
<td class="clientproductHeader" data-bind="text: $data"></td>
<td class="clientproductHeader" colspan="10"></td>
</tr>
<tbody data-bind="foreach: ko.observableArray($root.datainput()).extendsdistinct('Grade').index.Grade()[$data]">
<tr>
<script type="text/html" id="read-template">
<td data-bind="text: Type"></td>
<td data-bind="text: Volume"></td>
<td data-bind="text: (typeof Price() === 'number') ? Price().toFixed(2) : '' "></td>
</script>
</tr>
</tbody>
<!--/ko-->
</table>
The above script for some reasons display duplicate data like if a Grade is repeated two times as in the example data it displays the same data of that grade two times and if it repeated three times then displays it thrice and so on.
I thought the error was with <!--ko foreach: uniqueSelect-->
because it just loops the data depending on the number of similar grades.
results on html page are like
A
Buy 1000 10
Sell 1200 10
B
Sell 100 100
A
Buy 1000 10
Sell 1200 10
In the above data Grade A is repeated twice so all the data with grade A is repeated twice where as grade B has just one entry and so, it happened only once screen of data I received No clue how to deal with this
Upvotes: 0
Views: 101
Reputation: 2682
I went through and wrote a version that does what I think you are trying to accomplish. It uses the method Pēteris described. See it in action.
You want to keep the view code as simple as possible, and also be careful about reaching back into $root
. If you are positive you would like to do it with a filter, make one that does a similar grouping as below, and then iterate over each of its entries/children. Also, try to avoid declaring observables in your views.
Finally, you could combine groupByGrade()
and getGrades()
into a single function that returns an object with a property for each of the results. It would save an iteration cycle.
The view:
<tr>
<td class="clientproductHeader" data-bind="text: 'Grade ' + $data + ' (' + $root.log()[$data].length + ' trades)'"></td>
<td class="clientproductHeader" colspan="10"></td>
</tr>
<tbody data-bind="foreach: $root.log()[$data]">
<tr>
<td data-bind="text: type"></td>
<td data-bind="text: volume"></td>
<td data-bind="text: (typeof price === 'number') ? price.toFixed(2) : '' "></td>
<td class="actioncells">
<a class="btn btn-success" title="Edit" data-bind="click: $root.edit">Edit</a>
</td>
<td class="actioncells">
<a class="btn btn-danger" title="Delete " data-bind="click: $root.remove">Delete</a>
</td>
</tr>
</tbody>
<!--/ko-->
And the JavaScript:
function testViewModel() {
// Underscore and Lo-dash both have a function to group
// an object by key. That's what you want. Here is a
// really simple version.
function groupByGrade(data) {
return data.reduce(function(last, next) {
if (last[next.grade]) {
last[next.grade].push(next);
} else {
last[next.grade] = [next];
}
return last;
}, {});
}
// Get a unique list of the grades in the data to iterate over
function getGrades(data) {
return data.reduce(function(last, next) {
return !!~last.indexOf(next.grade) ? last : last + next.grade;
}, '').split('');
}
var rawData = [
{
grade: 'A',
type: 'Buy',
volume: 1000,
price: 10
}, {
grade: 'A',
type: 'Sell',
volume: 1200,
price: 10
}, {
grade: 'B',
type: 'Sell',
volume: 100,
price: 100
}
];
console.log(getGrades(rawData));
this.log = ko.observable(groupByGrade(rawData));
this.grades = ko.observableArray(getGrades(rawData));
}
ko.applyBindings(new testViewModel());
Upvotes: 1