Search code examples
javascriptag-gridvega-liteobservablehq

AG Data Grid table: generate a Vega-Lite chart that updates automatically


I would like to use Ag Data Grid filtered data to generate plots in Vega-Lite that update automatically whenever the filter(s) are changed. I would prefer to have the implementation in an observablehq notebook. Here is a section on how to access the table data in Ag Grid documentation. I created an observablehq notebook where the chart is updated using a button. There are two issues with this implementation:

  1. The chart is generated only after the button is clicked.
  2. I would prefer the chart update to be automatic without the need to click a button.

Solution

  • You can make the AG Grid table an Observable “view” so that you can both (1) see the table and (2) refer to its value in other cells, which will re-run when you filter the table. Once you do that, it'll work naturally with anything else you want to do in the notebook. Here's a working example with Observable Plot, and here's the same example with Vega-Lite.

    Assuming you've loaded AgGrid and have gridOptions in a different cell, you can wrap AgGrid as an Observable view like this:

    viewof table = {
      const node = htl.html`<div style="height:320px;" class="ag-theme-alpine"></div>`;
      
      new AgGrid.Grid(node, gridOptions);
      gridOptions.api.addEventListener("modelUpdated", update) // set value after filter
      gridOptions.api.sizeColumnsToFit();
      update(); // set initial value
    
      function update() { // for Observable dataflow
        let value = [];
        gridOptions.api.forEachNodeAfterFilter(d => value.push(d.data));
        node.value = value;
        node.dispatchEvent(new Event("input"));
      }
      
      return node;
    }
    

    A few notes on how this is different from your version (as of when I saw it):

    • I put the HTML and the AgGrid initialization in the same cell, so that AgGrid has an object reference to the node and if you re-run the cell it all gets cleaned up and recreated as a whole.
    • I listen for when the grid rows change with gridOptions.api.addEventListener("modelUpdated", update).
    • In that update function, I do the same iteration over the rows that you were doing to build an array, but then I set it as the value of the node, and dispatch an input event from that node. That's what Observable looks for to tell when a view has changed its value and other cells should re-run.
    • I name the cell viewof table. You might've seen this pattern with sliders and other inputs on Observable. The viewof keyword means that you get the DOM node rendered, but you can also refer to its value with just table in other cells.

    Thanks for the question; I’ve been meaning to check out AG Grid for a while!