Search code examples
javascripttabulator

In tabulator how to use selected data for bottom row calcs


I have a fairly simple table, and am currently using a bottom calculator formatter:

export let myTable = new Tabulator("#my-table", {
  columns:[
      {title:"ID", field:"id", headerSort:false, visible:false, responsive:2},
      {formatter:"rowSelection", titleFormatter:"rowSelection", align:"center", bottomCalc:"sum", hozAlign:"center", headerSort:false, cellClick:function(e, cell){
        cell.getRow().toggleSelect();
      }},
      {title:"Name", field:"address", width:300, bottomCalc:"count"},
      {title:"My Data", field:"mydata", bottomCalc:avNoOutsiders},
      ],
});
export let avNoOutsiders = function(values, data, calcParams){
  // filter outliers
  let myArray = filterOutliers(values);
  // filter any null or falsy values
  let av = average(myArray);
  return av
}

The code isn't super important, but what I'd like to be able to do is allow the user to de-select a row to exclude the value from this calculation.

The problem is, I don't understand how to access the isSelected() function here, I think it's just the row() I can access it. I can access the values (all the column values) but there's no selection data there, I can access the data - the whole table, but there's no way of determining which row it is, or whether it is selected or not.

My current direction of thinking is either

  1. using bottomCalcParams. I don't understand how I would do this. This function returns a getRow() is not a function error:
function cellIsSelected(cell){
  selected = cell.getRow().isSelected()
  return {isSelected:selected};
}

or

  1. Writing individual functions for each bottom calc. This doesn't work as I can't call the table inside the table calcs - var selectedRows = table.getSelectedRows() causes a circular error if I try to put that into a column calc function. I can reference the table inside the table.

Any ideas how I can access the row selection data to make a column calculation?


Solution

  • There might be an easier way, but one way to achive that would be to create a hidden placeholder column which can be updated to true/false upon row selection/deselection. Then you can use the values of this hidden column in your bottomCalc function to exclude rows that are not selected. Here is an example where selecting a row will re-trigger bottom calculation and average the age of all selections:

    const dataSet1 = [
      { id: 1, name: 'Billy Bob', age: '21', gender: 'male' },
      { id: 2, name: 'Mary May', age: '5', gender: 'female' },
      { id: 3, name: 'Christine Lobowski', age: '42', gender: 'female' },
      { id: 4, name: 'Brendon Philips', age: '80', gender: 'male' },
    ]
    
    const calcAvg = (values, data, calcParams) => {
      let selected = data.filter((row) => row.isSelected)
    
      values = selected.map((i) => i.age)
    
      let avg = values.reduce((a, b) => Number(a) + Number(b), 0) / values.length
    
      return avg ? avg : ''
    }
    
    const table = new Tabulator('#table', {
      data: dataSet1,
      columns: [
        {
          formatter: 'rowSelection',
          titleFormatter: 'rowSelection',
          cellClick: (e, cell) => {
            cell.getRow().toggleSelect()
          }
        },
        { title: 'Name', field: 'name' },
        { title: 'Age', field: 'age', bottomCalc: calcAvg },
        { title: 'Gender', field: 'gender' },
        { title: '', field: 'isSelected', visible: false }  // Selection placeholder column
      ]
    })
    
    const selection = (row) => {
      row.update({ isSelected: row.isSelected() })
      table.recalc()
    }
    
    table.on('rowSelected', selection)
    table.on('rowDeselected', selection)
    <html>
    
    <head>
      <link href="https://unpkg.com/[email protected]/dist/css/tabulator.min.css" rel="stylesheet">
      <script type="text/javascript" src="https://unpkg.com/[email protected]/dist/js/tabulator.min.js"></script>
    </head>
    
    <body>
      <div id="table"></div>
    </body>
    <html>