Search code examples
jqueryjqgridfree-jqgrid

Free JqGrid, calculated field is wrongly set to NaN in the footer


I am trying to work out why the percentage number in the field in the corner of this grid is set to NaN. To save you time, look at the JsFiddle here; https://jsfiddle.net/arame3333/ttc83khL/8/

Alternatively if the link is broken here is the code; HTML:

<body>
    <hi>SIR Tracker - Outstanding Defects Current on 23rd November 2017</hi>
    <div id="outerDiv" style="margin:5px;">
      <table id="treegrid"></table>
  </div>
</body>

CSS

.ui-jqgrid .ui-jqgrid-htable th div, .ui-jqgrid-sortable  {
    height:auto;
    overflow:hidden;
    white-space:normal !important;
}

JQuery/JqGrid:

$(function () {
    "use strict";
    var thisMonthName = getMonthName();
    var lastMonthName = getLastMonthName();
    var mydata = [
        { id: "1", name: "CD/N142 Janson Close", yearStart: "10", lastMonth: "1",
            thisMonth: "72", outstandingFaults: "83", openFaults: "83", closeFaults: "3", totalFaults: "169",
                  level: "0", parent: "null", isLeaf: false, expanded: true, loaded: true,
            icon: "ui-icon-folder-open,ui-icon-folder-collapsed" },
        { id: "2", name: "Electrical", yearStart: "8", lastMonth: "1",
            thisMonth: "48", outstandingFaults: "77", openFaults: "43", closeFaults: "2", totalFaults: "109",
            level: "1", parent: "1", isLeaf: true, expanded: false, loaded: true },
        { id: "3", name: "Mechanical", yearStart: "2", lastMonth: "0",
            thisMonth: "24", outstandingFaults: "6", openFaults: "40", closeFaults: "1", totalFaults: "60",
            level: "1", parent: "1", isLeaf: true, expanded: false, loaded: true },
      { id: "4", name: "CD/L224 Lychet Way", yearStart: "3", lastMonth: "10",
            thisMonth: "67", outstandingFaults: "80", openFaults: "130", closeFaults: "265", totalFaults: "475",
                  level: "0", parent: "null", isLeaf: false, expanded: true, loaded: true,
            icon: "ui-icon-folder-open,ui-icon-folder-collapsed" },
        { id: "5", name: "Electrical", yearStart: "2", lastMonth: "8",
            thisMonth: "47", outstandingFaults: "40", openFaults: "80", closeFaults: "190", totalFaults: "270",
            level: "1", parent: "4", isLeaf: true, expanded: false, loaded: true },
        { id: "6", name: "Mechanical", yearStart: "1", lastMonth: "2",
            thisMonth: "20", outstandingFaults: "40", openFaults: "50", closeFaults: "75", totalFaults: "205",
            level: "1", parent: "4", isLeaf: true, expanded: false, loaded: true }
    ];

    $("#treegrid").jqGrid({
        datatype: "local",
        data: mydata,
        colNames: ["Project", "From year start", lastMonthName, thisMonthName, "Outstanding faults", "Open", "Close", "Total", "% Outstanding"],
        colModel: [
            { name: "name", width: 200 },
            { name: "yearStart", template: "integer", width: 50 },
            { name: "lastMonth", template: "integer", width: 50 },
            { name: "thisMonth", template: "integer", width: 50 },
            { name: "outstandingFaults", template: "integer", width: 80 },
            { name: "openFaults", template: "integer", width: 50 },
            { name: "closeFaults", template: "integer", width: 50 },
            { name: "totalFaults", template: "integer", width: 50 },
            { name: 'faultsPerc', template: "integer", width: 80,
                formatter: function (cellvalue, options, rowObject) 
                         {
                             return Math.round(rowObject["outstandingFaults"] / rowObject["totalFaults"] * 100);
                          }
                        }
        ],
        cmTemplate: { autoResizable: true },
        autoresize: true,
        rownumbers: true,
        viewrecords: true,
        autoResizing: { compact: true },
        treeGrid: true,
        treeGridModel: "adjacency",
        ExpandColumn: "name",
        footerrow: true,
        gridComplete: function() {
          var $grid = $('#treegrid');
          $grid.jqGrid('footerData', 'set', { "name": "Total"} );
          var colSumYearStart = $grid.jqGrid('getCol', "yearStart", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "yearStart": colSumYearStart });
          var colSumLastMonth = $grid.jqGrid('getCol', "lastMonth", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "lastMonth": colSumLastMonth });
          var colSumThisMonth = $grid.jqGrid('getCol', "thisMonth", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "thisMonth": colSumThisMonth });
          var colSumOutstandingFaults = $grid.jqGrid('getCol', "outstandingFaults", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "outstandingFaults": colSumOutstandingFaults });
          var colSumOpenFaults = $grid.jqGrid('getCol', "openFaults", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "openFaults": colSumOpenFaults });
          var colSumCloseFaults = $grid.jqGrid('getCol', "closeFaults", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "closeFaults": colSumCloseFaults });
          var colSumCloseFaults = $grid.jqGrid('getCol', "closeFaults", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "closeFaults": colSumCloseFaults });
          var colSumTotalFaults = $grid.jqGrid('getCol', "totalFaults", false, 'sum') / 2;
          $grid.jqGrid('footerData', 'set', { "totalFaults": colSumTotalFaults });
          var avePercentage = colSumOutstandingFaults / colSumTotalFaults * 100;
          $grid.jqGrid('footerData', 'set', { "faultsPerc": avePercentage });
      }

    });
});

var populateFooterTotals = function(item){
    var $grid = $('#treegrid');
  var colSum = $grid.jqGrid('getCol', item, false, 'sum') / 2;
  $grid.jqGrid('footerData', 'set', { item: colSum });
}

var getMonthName = function(){
    var d = new Date();
  var n = d.getMonth();
  return getMonthNameByIndex(n);
}

var getLastMonthName = function(){
    var d = new Date();
  var n = d.getMonth() - 1;
  if(n === -1){
    n = 12;
  }

  return getMonthNameByIndex(n);
}
var getMonthNameByIndex = function(index){
  var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June',
   'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
   return months[index];
}

Solution

  • You should use one call of footerData method instead of multiple calls. It's more effective, first of all, and it's important because of the code of formatter of faultsPerc column uses the content of outstandingFaults and totalFaults properties of the same row. footerData method has 3 parameter. If you don't specify false as the last parameter then the data will be formatted with respect of formatters of the corresponding columns. Thus it's important that outstandingFaults and totalFaults properties will be defined in footerData.

    loadComplete: function() {
      var $grid = $(this);
      var colSumYearStart = $grid.jqGrid('getCol', "yearStart", false, 'sum') / 2;
      var colSumLastMonth = $grid.jqGrid('getCol', "lastMonth", false, 'sum') / 2;
      var colSumThisMonth = $grid.jqGrid('getCol', "thisMonth", false, 'sum') / 2;
      var colSumOutstandingFaults = $grid.jqGrid('getCol', "outstandingFaults", false, 'sum') / 2;
      var colSumOpenFaults = $grid.jqGrid('getCol', "openFaults", false, 'sum') / 2;
      var colSumCloseFaults = $grid.jqGrid('getCol', "closeFaults", false, 'sum') / 2;
      var colSumTotalFaults = $grid.jqGrid('getCol', "totalFaults", false, 'sum') / 2;
      var avePercentage = colSumOutstandingFaults / colSumTotalFaults * 100;
      $grid.jqGrid('footerData', 'set', {
        name: "Total",
        yearStart: colSumYearStart,
        lastMonth: colSumLastMonth,
        thisMonth: colSumThisMonth,
        outstandingFaults: colSumOutstandingFaults,
        openFaults: colSumOpenFaults,
        closeFaults: colSumCloseFaults,
        totalFaults: colSumTotalFaults,
        faultsPerc: avePercentage
      });
    }
    

    Additionally you can consider to use jsonmap instead of custom formatter:

    { name: 'faultsPerc', formatter: "currency", width: 80,
        formatoptions: { suffix: "%", decimalPlaces: 0 },
        align: "right", sorttype: "integer",
        searchoptions: { sopt: ["eq", "ne", "lt", "le", "gt", "ge"] },
        jsonmap: function (item) {
            return Math.round(item.outstandingFaults / item.totalFaults * 100);
        }
    }
    

    jsonmap will "gets" the faultsPerc property from the input data and save it in the local item data. You can then use any standard formatter, like formatter: "currency". See https://jsfiddle.net/OlegKi/ttc83khL/9/