Группа По сумме элементов в наблюдаемом массиве нокаута

0 Saurabh [2016-07-06 19:08:00]

Мои скрипты PHP возвращаются после JSON, который затем вставляется в наблюдаемый массив нокаута:

[{"distributor":"DIS A","onlyYear":"2013","Net_Royalty":"10200","Phase":null},{"distributor":"DIS A","onlyYear":"2016","Net_Royalty":"210","Phase":null},{"distributor":"DIS V","onlyYear":"2013","Net_Royalty":"13600","Phase":null},{"distributor":"DIS D","onlyYear":"2013","Net_Royalty":"10200","Phase":null}]

Часть кода JS для создания наблюдаемого массива:

    function reportModel()
      {
        var self = this;
        this.reports = ko.observableArray();
        $.ajax({
              url: url+"query_report_data",
              type: "post",
              cache: false,
              success: function(query_result)
              {
                var data = $.parseJSON(query_result);
                self.reports(data);
              }
            });
      });
ko.applyBindings(new reportModel());

В HTML я использую нокаут JS foreach для представления таблицы с использованием данных из наблюдаемого массива:

<tbody data-bind="visible: reports().length > 0">
            <!-- ko foreach: reports -->
            <tr>
                <td class="text-center" data-bind="text: distributor"></td>
                <td class="text-center" data-bind="text: onlyYear"></td>
                <td class="text-right"><mark><span data-bind="text: Net_Royalty"></span> INR</mark></td>
            </tr>
            <!-- /ko -->
</tbody>

Все работает нормально. Я получаю следующую таблицу в браузере:

Distributor|Year|Net Royalty
DIS A | 2013 | 10200
DIS A | 2016 | 210
DIS V | 2013 | 13600
DIS D | 2013 | 10200

Вместо этого я хочу выполнить вывод в браузере:

Distributor|Year|Net Royalty
DIS A | 2013 | 10200
DIS A | 2016 | 210
Sum of DIS A| 10410
DIS V | 2013 | 13600
Sum of DIS V| 13600
DIS D | 2013 | 10200
Sum of DIS D| 10200

Я хочу добавить дополнительную строку после дистрибьюторов, чтобы показать сумму чистой роялти для этого дистрибьютора. Как это сделать?

javascript knockout.js


2 ответа


0 Regis Portalez [2016-07-07 10:38:00]

Сначала я бы немного сконфигурировал структуру и представил несколько моделей просмотра, соответствующих вашей бизнес-логике.

Я не понимаю этого, поэтому я попытался представить distributorViewModel, который объединяет подпункты.

Затем вы можете добавить вычисленное свойство на этой виртуальной машине более высокого уровня, чтобы отобразить общее количество, которое вы хотите:

var array = [{"distributor":"DIS A","onlyYear":"2013","Net_Royalty":"10200","Phase":null},{"distributor":"DIS A","onlyYear":"2016","Net_Royalty":"210","Phase":null},{"distributor":"DIS V","onlyYear":"2013","Net_Royalty":"13600","Phase":null},{"distributor":"DIS D","onlyYear":"2013","Net_Royalty":"10200","Phase":null}];

var itemViewModel = function(rawItem) {
  // all raw items should first be validated by a model of some kind. 
  // let trust the server, as this is just a sample
  this.name = rawItem.distributor;
  this.onlyYear = parseInt(rawItem.onlyYear);
  this.royalty = parseInt(rawItem.Net_Royalty);
};

var distributorViewModel = function(name) {
  var self = this;
  this.name = name;
  this.items = ko.observableArray([]);
  this.royalty = 0; // could be computed somehow
}

var listViewModel = function() {
  var self = this;
  self.distributors = ko.observableArray([]);
  var cache = {};
  for(var i = 0; i < array.length; ++i) {// should be in your success callback
    var distributorName = array[i].distributor;
    var itemVM = new itemViewModel(array[i]);
    if(!cache[distributorName]) {
      cache[distributorName] = new distributorViewModel(distributorName);
    }
    cache[distributorName].items().push(itemVM);
    cache[distributorName].royalty += itemVM.royalty;
  }
  
  for(var key in cache) {
    self.distributors.push(cache[key]);
  }
};


ko.applyBindings(new listViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>


<div data-bind="foreach: distributors">
  <div data-bind="text: name"></div>
  <div data-bind="foreach: items">
    <div data-bind="text: 'year : ' + onlyYear"></div>
    <div data-bind="text: 'royalty: ' + royalty"></div>
  </div>
  <div data-bind="text: 'total: ' + royalty"></div>
  <hr>
</div>

0 user3297291 [2016-07-07 10:50:00]

Помимо всех видов архитектурных изменений, вы, вероятно, могли бы управлять одним ko.computed:

var testData = [{
  "distributor": "DIS A",
  "onlyYear": "2013",
  "Net_Royalty": "10200",
  "Phase": null
}, {
  "distributor": "DIS A",
  "onlyYear": "2016",
  "Net_Royalty": "210",
  "Phase": null
}, {
  "distributor": "DIS V",
  "onlyYear": "2013",
  "Net_Royalty": "13600",
  "Phase": null
}, {
  "distributor": "DIS D",
  "onlyYear": "2013",
  "Net_Royalty": "10200",
  "Phase": null
}];

var VM = function(data) {
  var self = this;

  this.reports = ko.observableArray(data.map(function(r) {
    // Quicky add a label because I don't want to prototype the template
    return Object.assign(r, {
      label: [r.distributor, r.Net_Royalty].join(" ")
    });
  }));

  this.groupedReports = ko.computed(function() {
    var royalty = 0;

    return self.reports().reduce(function(result, currentReport, i, reports) {
      var nextReport = reports[i + 1];
      result.push(currentReport);
      
      // Keep track of the sum of royalties outside function
      royalty += parseInt(currentReport.Net_Royalty, 10);

      // If it the last report, or the next report has a different distributor
      if (!nextReport || nextReport.distributor !== currentReport.distributor) {
        result.push(royalty);
        royalty = 0;
      }
      
      
      return result;
    }, []);

  });
}

var vm = new VM(testData);
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: groupedReports">
  <li data-bind="text: $data.label || $data">

  </li>
</ul>