DoIt
DoIt

Reputation: 3428

Incorrect values from a knockout observable array

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 receivedenter image description here No clue how to deal with this

Upvotes: 0

Views: 101

Answers (1)

Joshua
Joshua

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

Related Questions