Search code examples
javascriptgoogle-visualization

Access Google Visualization from another function


I have the below google dashboard with filtering, sorting, and paging.

I will modify the sourceData programmatically. I need to be able to access the google visualization in order to refresh it from outside it's containing function.

For example: renderChart_onPageLoad contains all the google code. I need to access it from the function outsideGV. Normally inside renderChart_onPageLoad I would call table.draw(); but I can't get access that from function outsideGV. I must maintain any user paging, sorting, and filtering between refreshes.

FYI...I did try calling renderChart_onPageLoad function again but it wiped out all the user inputs for paging, sorting, filtering and just reset the visualization.

I would appreciate any feedback.

UPDATE I am unlikely to have one existence on a variable (data for example) on a given page. I have a variable called data for every chart on my page.

I have many google tables on the page drawn by the same drawDashboard_ function. Is it possible to collect the references in a global array?

const globalReferences = [];

I would like to fill it as each drawDashboard_ is executed. Could this collect the variables after they are all created?

globalReferences.push({
  "chartID":suffix, //this is my unique identifier for each pass of the function
  "data": data,
  "dashboard": dashboard,
  "categoryPicker": categoryPicker,
  "proxyTable": proxyTable,
  "table": table
});

Can I take a reference from here to access a specific chart?

   globalReferences[0].table; 
//get the reference, make some change, and then redraw.

google.charts.load('current', {
  'packages': ['corechart', 'table', 'gauge', 'controls', 'charteditor']
});

var listenerPage = {};
var listenerSort = {};

var sourceData = [
  ['Name', 'RoolNumber', 'Gender', 'Age', 'DonutsEaten'],
  ['Michael', 1, 'Male', 12, 5],
  ['Elisa', 2, 'Female', 20, 7],
  ['Robert', 3, 'Male', 7, 3],
  ['John', 4, 'Male', 54, 2],
  ['Jessica', 5, 'Female', 22, 6],
  ['Aaron', 6, 'Male', 3, 1],
  ['Margareth', 7, 'Female', 42, 8],
  ['Miranda', 8, 'Female', 33, 6]
]

$(document).ready(function() {
  renderChart_onPageLoad();
});

function referenceGV() {

  //ON BUTTON CLICK 
  //1 UPDATD sourceData - Make a change programatically
  //1 REFRESH GV
  //    must retaining all selected user filtering, sorting, paging

}

function renderChart_onPageLoad() {
  google.charts.setOnLoadCallback(function() {
    drawDashboard_A("A");
  });
}

function drawDashboard_A(suffix) {

  var data = google.visualization.arrayToDataTable(sourceData);

  var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_' + suffix));

  var categoryPicker = new google.visualization.ControlWrapper({
    controlType: 'CategoryFilter',
    containerId: 'categoryPicker_' + suffix,
    options: {
      filterColumnLabel: 'Gender',
      ui: {
        labelStacking: 'vertical',
        allowTyping: false,
        allowMultiple: false
      }
    }
  });

  var proxyTable = new google.visualization.ChartWrapper({
    chartType: 'Table',
    containerId: 'proxyTable_' + suffix,
    options: {
      width: '500px'
    }
  });

  var table = new google.visualization.ChartWrapper({
    chartType: 'Table',
    containerId: 'table_' + suffix,
    options: {
      sort: 'event', // <-- set sort to 'event' for table totaling
      width: '500px',
      allowHtml: true,
      page: 'enable',
      pageSize: '3',
    }
  });

  //This json contains my settings for later
  let json = {
    "tableChart": {
      "hasTable": true,
      "dataView": {
        "columns": [{
            "id": "Name"
          },
          {
            "id": "RoolNumber"
          },
          {
            "id": "Gender"
          },
          {
            "id": "Age"
          },
          {
            "id": "DonutsEaten"
          }
        ]
      },
      "clickGetFunc": clickGetFunc
    },
  };

  function clickGetFunc(result, wrapperDataTable) {
    console.log(result, wrapperDataTable)
  }

  google.visualization.events.addOneTimeListener(proxyTable, 'ready', function() {
    //console.log(suffix + ".addOneTimeListener(proxyTable, 'ready' - sort");

    google.visualization.events.addOneTimeListener(table, 'ready', function() {
      //console.log(suffix + ".addOneTimeListener(table, 'ready' - sort");

      //#region table - sort: 'event'
      google.visualization.events.addListener(table.getChart(), 'sort', function(sender) {
        console.log(suffix + ".addListener(table.getChart(), 'sort' - sorted");

        // sort data table according to sort properties
        var tableData = table.getDataTable();
        var sortIndexes = tableData.getSortedRows({
          column: sender.column,
          desc: !sender.ascending
        });

        // set table sort arrow
        table.setOption('sortAscending', sender.ascending);
        table.setOption('sortColumn', sender.column);

        // set table view & re-draw table
        table.setView({
          rows: sortIndexes
        });
        table.draw();

        //Table ready then fires
      });
      //#endregion

    });
  });

  google.visualization.events.addOneTimeListener(proxyTable, 'ready', function() {
    google.visualization.events.addListener(table, 'ready', function() {
      lib_gcharts_selectedRowCol(table, json.tableChart.clickGetFunc); //(wrapperName, callback)
    });
  });

  google.visualization.events.addListener(proxyTable, 'ready', function() {
    refGV = proxyTable;

    redrawTable(json.tableChart);
  });

  dashboard.bind([categoryPicker], [proxyTable]);
  dashboard.draw(data);

  //This is a table builder which uses json from above.  This is working ok.  Contains no listeners.
  function redrawTable(tableChart) {

    // set defaults for any undefined settings
    let dataView = (tableChart.dataView !== undefined) ? tableChart.dataView : [];

    // update .draw() 'table' or 'chart' references when using a different or additional chart name

    var sourceData = proxyTable.getDataTable().toDataTable().clone();
    //console.log('sourceData', sourceData);

    //#region create data view - this is used as basis for dataResults
    let view = new google.visualization.DataView(sourceData);

    //#region create group view - if required
    let dataResults_forTable;
    dataResults_forTable = view.toDataTable().clone();

    //#endregion

    var finalView_forTable = new google.visualization.DataView(dataResults_forTable);
    //console.log('finalView_forTable', finalView_forTable);

    // set reset sorting, set dataTable & draw chart
    table.setView(null); // reset in case sorting has been used via user click
    table.setDataTable(finalView_forTable); //includes any total row
    table.draw();

  } //END redrawChart()

}



function lib_gcharts_selectedRowCol(chartWrapper, callback, clicks) {
  //console.log('chartWrapper', chartWrapper); console.log('callback', callback)

  //2021-07-26 added clicks functionality (click or dblclick)

  //NEW - Works with paging active
  // initialize page number and size
  var page = 0;
  var pageSize = 10;
  if (chartWrapper.getOption('page') === 'enable') {
    page = chartWrapper.getOption('startPage');
    pageSize = chartWrapper.getOption('pageSize');
  }
  enableCoordinates(callback);

  // remove previous event handlers if they exist 
  if (listenerPage.hasOwnProperty(chartWrapper.getContainerId())) {
    google.visualization.events.removeListener(listenerPage[chartWrapper.getContainerId()]);
    delete listenerPage[chartWrapper.getContainerId()];
  }
  if (listenerSort.hasOwnProperty(chartWrapper.getContainerId())) {
    google.visualization.events.removeListener(listenerSort[chartWrapper.getContainerId()]);
    delete listenerSort[chartWrapper.getContainerId()];
  }

  // page event - assigns chartWrapper.getContainerId() as unique identifier for listener
  listenerPage[chartWrapper.getContainerId()] = google.visualization.events.addListener(chartWrapper.getChart(), 'page', function(sender) {
    //console.log(".addListener(chartWrapper.getChart(), 'page' - paged - enableCoordinates called");
    page = sender.page; // save current page
    enableCoordinates(callback);
  });

  // sort event - assigns chartWrapper.getContainerId() as unique identifier for listener
  listenerSort[chartWrapper.getContainerId()] = google.visualization.events.addListener(chartWrapper.getChart(), 'sort', function() {
    //console.log(".addListener(chartWrapper.getChart(), 'sort' - sorted - enableCoordinates called ")
    page = 0; // reset back to first page
    enableCoordinates(callback);
  });

  function enableCoordinates(callback) {
    //console.log('enableCoordinates', callback);

    var container = document.getElementById(chartWrapper.getContainerId());

    // remove existing enableCoordinates event listeners from all TD elements
    Array.prototype.forEach.call(container.getElementsByTagName('TD'), function(cell) {
      //cell.removeEventListener("dblclick", selectCell, false);//2021-07-21
      cell.removeEventListener(clicks, selectCell, false);
    });

    // add new enableCoordinates event listeners to all TD elements
    Array.prototype.forEach.call(container.getElementsByTagName('TD'), function(cell) {
      //cell.addEventListener('dblclick', selectCell, false);//2021-07-21
      cell.addEventListener(clicks, selectCell, false);
    });
  }

  function selectCell(event) {
    //console.log('sender', sender);

    //2021-07-26 re-write for google crud
    //1. renamed parameter "sender" to "event" for standard reference 
    //Consider building in the functionality for google crud

    //var cell = sender.target;

    //Get selected cell TD element
    var cell = event.target;
    if (event.target.tagName === 'I') {
      cell = event.target.parentNode; //if icon clicked set cell = parentNode for TD element
    }
    var row = cell.closest('tr');

    var wrapperDataTable = chartWrapper.getDataTable();

    var selectedRow = row.rowIndex - 1; // adjust for header row (-1)
    selectedRow = (page * pageSize) + selectedRow; // adjust for page number

    // Original from whitehat - save sorted info
    // This version does not work after a user clicks a column to sort
    //var sortInfo = chartWrapper.getChart().getSortInfo();  
    //if (sortInfo.sortedIndexes !== null) {
    //    selectedRow = sortInfo.sortedIndexes[selectedRow];
    //}  

    // Replaced code to which finds the view row order by .getView then taking the selected row 
    // and returning the number which is in the .getView result
    var sortInfo = chartWrapper.getView(); // save sorted info
    if (sortInfo !== null) {
      selectedRow = sortInfo.rows[selectedRow];
    }
    var selectedCol = cell.cellIndex;

    //var result = "selectedRow: " + selectedRow + " selectedCol: " + selectedCol;
    //var ul = document.getElementById("demo");
    //var li = document.createElement("li");
    //li.innerHTML = result;
    //ul.appendChild(li);

    var selectedValue = wrapperDataTable.getValue(selectedRow, selectedCol);
    var colID = wrapperDataTable.getColumnId(selectedCol);
    var colLabel = wrapperDataTable.getColumnLabel(selectedCol);

    var result = {
      "selectedRow": selectedRow,
      "selectedCol": selectedCol,
      "selectedValue": selectedValue,
      "colID": colID,
      "colLabel": colLabel,
      "event": event, //2021-07-26
      "chartWrapper": chartWrapper //2021-07-9
    };

    callback(result, wrapperDataTable);
  }
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="dashboardA">
  <div id="categoryPicker_A"></div><br />
  <div id="proxyTable_A" style="display:none;"></div>
  <div id="table_A"></div><br /><br />
</div>

<button onclick="referenceGV()">
referenceGV
</button>


Solution

  • just need to declare your variables in the global space, similar to your sourceData...

    and remove the var statement from the original declarations.

    just be sure not to try to access the variables before they have been created...

    google.charts.load('current', {
      'packages': ['corechart', 'table', 'gauge', 'controls', 'charteditor']
    });
    
    var listenerPage = {};
    var listenerSort = {};
    
    var sourceData = [
      ['Name', 'RoolNumber', 'Gender', 'Age', 'DonutsEaten'],
      ['Michael', 1, 'Male', 12, 5],
      ['Elisa', 2, 'Female', 20, 7],
      ['Robert', 3, 'Male', 7, 3],
      ['John', 4, 'Male', 54, 2],
      ['Jessica', 5, 'Female', 22, 6],
      ['Aaron', 6, 'Male', 3, 1],
      ['Margareth', 7, 'Female', 42, 8],
      ['Miranda', 8, 'Female', 33, 6]
    ]
    
    // declare variables here
    var data;
    var dashboard;
    var categoryPicker;
    var proxyTable;
    var table;
    
    
    $(document).ready(function() {
      renderChart_onPageLoad();
    });
    
    function referenceGV() {
    
      //ON BUTTON CLICK 
      //1 UPDATD sourceData - Make a change programatically
      //1 REFRESH GV
      //    must retaining all selected user filtering, sorting, paging
    
    }
    
    function renderChart_onPageLoad() {
      google.charts.setOnLoadCallback(function() {
        drawDashboard_A("A");
      });
    }
    
    function drawDashboard_A(suffix) {
    
      // remove var statement from here
      data = google.visualization.arrayToDataTable(sourceData);
    
      dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_' + suffix));
    
      categoryPicker = new google.visualization.ControlWrapper({
        controlType: 'CategoryFilter',
        containerId: 'categoryPicker_' + suffix,
        options: {
          filterColumnLabel: 'Gender',
          ui: {
            labelStacking: 'vertical',
            allowTyping: false,
            allowMultiple: false
          }
        }
      });
    
      proxyTable = new google.visualization.ChartWrapper({
        chartType: 'Table',
        containerId: 'proxyTable_' + suffix,
        options: {
          width: '500px'
        }
      });
    
      table = new google.visualization.ChartWrapper({
        chartType: 'Table',
        containerId: 'table_' + suffix,
        options: {
          sort: 'event', // <-- set sort to 'event' for table totaling
          width: '500px',
          allowHtml: true,
          page: 'enable',
          pageSize: '3',
        }
      });