How to use the AG Grid API with Dash

How to use the AG Grid API with Dash

Overview

In this post, I’ll cover:

  • Accessing the Grid’s API in a Dash Clientside Callback
  • Determining the Correct AG Grid Docs Version for Dash AG Grid
  • Examples: Flashing Cells using the Grid’s API
  • Differentiating Between Dash AG Grid Functions: getApiAsync and getApi

Using the Grid’s API

The grid is highly configurable and has an extensive API. Not all AG Grid props and methods are defined as Dash props, but you can still use them.

Many advanced features require using the Grid’s API. You can access the grid’s API in Dash using getApi or
getApiAsync in a clientside callback. I’ll use getApiAsync in the next couple examples, then later explain the
difference between the functions.

Determining the Correct AG Grid Docs Version for Dash AG Grid

Note that not all the grid’s features are included in the Dash docs, so it’s necessary to also refer to AG Grid docs.
Given AG Grid’s frequent releases, you may often find it necessary to use an archived version of their docs.

Starting with Dash AG Grid V31.0.0, you can find your grid version by using dag.grid_version

import dash_ag_grid as dag 

print("dash-ag-grid version: ", dag.__version__)
print("AG Grid version: ", dag.grid_version)

Now, navigate to the AG Grid docs archive to find the appropriate documentation link. If you can’t locate an exact match, use the version one minor step lower.

In the following examples, I’ll show how to use the grid’s API to flash cells (a feature that is not yet in the Dash docs, but coming soon!). It’s important to use the archived version of AG Grid docs, because the in a later version, AG Grid changed some prop names.

In this tutorial I’m using Dash AG Grid V31.0.1 which uses grid version 31.0.3.

Please follow along using the AG Grid docs for Flashing Cells React Data Grid: Flashing Cells. This uses the AG Grid archived docs for V31.0.2

Example 1: Flashing Cells using the Grid’s API

The grid can flash cells to highlight data changes. This is a great visual indicator to users of the grid who want data changes to be noticed.

Here I’m just going to provide a Dash version of the examples from the AG Grid docs. Please refer to the AG Grid docs for all the details about the flashCells method.

ag-grid-get-api-tutorial


import dash_ag_grid as dag
from dash import Dash, html, Input, Output, clientside_callback
import random
import pandas as pd


df = pd.DataFrame({
    'a': [random.randint(0, 10000) for _ in range(20)],
    'b': [random.randint(0, 10000) for _ in range(20)],
    'c': [random.randint(0, 10000) for _ in range(20)],
    'd': [0] * 20,
    'e': [0] * 20,
    'f': [0] * 20
})


app = Dash()

app.layout = html.Div(
    [
        html.Button("Update Some Data", id="btn-flash-update"),
        html.Button("Flash One Cell", id="btn-flash-cell"),
        html.Button("Flash Two Rows", id="btn-flash-rows"),
        html.Button("Flash Two Columns", id="btn-flash-cols"),

        dag.AgGrid(
            id="grid-flash-cells",
            rowData=df.to_dict("records"),
            columnDefs=[{"field": i } for i in df.columns],
            defaultColDef={"flex": 1,"cellClass": 'align-right', "enableCellChangeFlash": True}

        )
    ]
)

# Update some data
clientside_callback(
    """async (n) => {
        if (n) {
            const gridApi =  await dash_ag_grid.getApiAsync("grid-flash-cells")

            var rowCount = gridApi.getDisplayedRowCount();        
            // pick 20 cells at random to update
            for (var i = 0; i < 20; i++) {
              var row = Math.floor(Math.random() * rowCount);
              var rowNode = gridApi.getDisplayedRowAtIndex(row);
              var col = ['a', 'b', 'c', 'd', 'e', 'f'][i % 6];
              rowNode.setDataValue(col, Math.floor(Math.random() * 10000));
            }        
        }
        return dash_clientside.no_update
    }""",
    Output("btn-flash-update", "id"),
    Input("btn-flash-update", "n_clicks"),
)


# Flash cell
clientside_callback(
    """async  (n) => {
        if (n) {
            const gridApi =  await dash_ag_grid.getApiAsync("grid-flash-cells")
            
            // flash row 4 col 'c'
            var rowNode = gridApi.getDisplayedRowAtIndex(4);
            gridApi.flashCells({ rowNodes: [rowNode], columns: ['c'] });
        }
        return dash_clientside.no_update
    }""",
    Output("btn-flash-cell", "id"),
    Input("btn-flash-cell", "n_clicks"),
)

# Flash 2 rows
clientside_callback(
    """async (n) => {
        if (n) {
            const gridApi =  await dash_ag_grid.getApiAsync("grid-flash-cells")

            // pick row 4 and 5
            var rowNode1 = gridApi.getDisplayedRowAtIndex(4);
            var rowNode2 = gridApi.getDisplayedRowAtIndex(5);
            
            // flash whole row, so leave column selection out
            gridApi.flashCells({ rowNodes: [rowNode1, rowNode2] });
        }
        return dash_clientside.no_update
    }""",
    Output("btn-flash-rows", "id"),
    Input("btn-flash-rows", "n_clicks"),
)


# Flash 2 columns
clientside_callback(
    """async (n) => {
         if (n) {
           const gridApi = await dash_ag_grid.getApiAsync("grid-flash-cells");
           gridApi.flashCells({ columns: ['c', 'd'] });
        }
        return dash_clientside.no_update;
    }""",
    Output("btn-flash-cols", "id"),
    Input("btn-flash-cols", "n_clicks"),
)


if __name__ == "__main__":
    app.run(debug=True)

Let’s take a closer look at the callback function to flash the columns.

In the AG Grid docs (React), they get the grid’s API using gridRef.current.api:

const onFlashTwoColumns = useCallback(() => {
    // flash whole column, so leave row selection out
    gridRef.current.api.flashCells({ columns: ['c', 'd'] });
  }, []);

In the Dash app above, getApiAsync is used to get the grid API. Note that you need to provide the id of grid. In this app the id is "grid-flash-cells"

const gridApi = await dash_ag_grid.getApiAsync("grid-flash-cells")

Here’s the Dash callback to flash 2 columns:

clientside_callback(
    """async (n) => {
         if (n) {
            const gridApi =  await dash_ag_grid.getApiAsync("grid-flash-cells")
            // flash whole column, so leave row selection out
            gridApi.flashCells({ columns: ['c', 'd'] });
         }
        return dash_clientside.no_update
    }""",
    Output("btn-flash-cols", "id"),
    Input("btn-flash-cols", "n_clicks"),
)

Once you have defined the gridApi, you’ll have access to all the grid API methods in the Dash clientside callback.

Here’s an example of how to flash the ‘c’ and ‘d’ columns:

gridApi.flashCells({ columns: ['c', 'd'] });

This will get row number 4. See how this is used in other clientside callbacks in the app above.

gridApi.getDisplayedRowAtIndex(4);

Example 2 How Flashing Works

See details about this example in the AG Grid docs

This example shows how to further configure the flashing by changing the flash background color with CSS and adjusting the length of the flash in the grid’s flashCells API.

This also shows how important it is to use the correct version of the docs. In AG Grid V31.1.0 they changed the prop names from flashDelay to flashDuration and from fadeDelay to fadeDuration. However, those new props aren’t available in our version (31.0.3), so we need to continue to use the old prop names.

Here’s the CSS to change the flash background color:

.flash .ag-theme-alpine,
.flash .ag-theme-alpine-dark {
  --ag-value-change-value-highlight-background-color: #cc222244;
}

ag-grid-get-api-tutorial2


import dash_ag_grid as dag
from dash import Dash, html, Input, Output, clientside_callback
import random
import pandas as pd

print("dash-ag-grid version: ", dag.__version__)
print("AG Grid version: ", dag.grid_version)

df = pd.DataFrame({
    'a': [random.randint(0, 10000) for _ in range(20)],
    'b': [random.randint(0, 10000) for _ in range(20)],
    'c': [random.randint(0, 10000) for _ in range(20)],
    'd': [0] * 20,
    'e': [0] * 20,
    'f': [0] * 20
})


app = Dash()

app.layout = html.Div(
    [
        html.Button("Update Some Data", id="btn-flash-update-custom"),
        html.Button("Flash Two Rows", id="btn-flash-rows-custom"),


        dag.AgGrid(
            id="grid-flash-cells-custom",
            rowData=df.to_dict("records"),
            columnDefs=[{"field": i } for i in df.columns],
            defaultColDef={"flex": 1,"cellClass": 'align-right', "enableCellChangeFlash": True},
            dashGridOptions={"cellFlashDelay": 2000, "cellFadeDelay": 500}

        )
    ], className="flash",
)

# Update some data
clientside_callback(
    """async (n) => {
        if (n) {
            const gridApi =  await dash_ag_grid.getApiAsync("grid-flash-cells-custom")

            var rowCount = gridApi.getDisplayedRowCount();        
            // pick 20 cells at random to update
            for (var i = 0; i < 20; i++) {
              var row = Math.floor(Math.random() * rowCount);
              var rowNode = gridApi.getDisplayedRowAtIndex(row);
              var col = ['a', 'b', 'c', 'd', 'e', 'f'][i % 6];
              rowNode.setDataValue(col, Math.floor(Math.random() * 10000));
            }        
        }
        return dash_clientside.no_update
    }""",
    Output("btn-flash-update-custom", "id"),
    Input("btn-flash-update-custom", "n_clicks"),
)


# Flash 2 rows
clientside_callback(
    """async  (n) =>  {
        if (n) {
            const gridApi =  await dash_ag_grid.getApiAsync("grid-flash-cells-custom")

            // pick row 4 and 5
            var rowNode1 = gridApi.getDisplayedRowAtIndex(4);
            var rowNode2 = gridApi.getDisplayedRowAtIndex(5);
            
            // flash whole row, so leave column selection out
            gridApi.flashCells({ 
              rowNodes: [rowNode1, rowNode2],  
              flashDelay: 3000,
              fadeDelay: 2000, 
            });
        }
        return dash_clientside.no_update
    }""",
    Output("btn-flash-rows-custom", "id"),
    Input("btn-flash-rows-custom", "n_clicks"),
)


if __name__ == "__main__":
    app.run(debug=True)

Understanding getApi and getApiAsync in Dash AG Grid

In Dash AG Grid, there are two function for retrieving the grid API: getApi and getApiAsync.

Since most of us use Python more than JavaScript, here are some great articles from MDN on the difference between synchronous and asynchronous functions in JavaScript:

getApi Method:

You can use getApi with a regular (synchronous) JavaScript function. It simply checks one time if the grid is ready and returns either the grid’s API or an error. This means that if the Dash callback is triggered when the app starts and the grid is not ready, you will get the error.

Here’s an example to illustrate. This callback will be triggered when the app starts and the grid won’t be ready.

# This uses getApi before the grid is ready and it  won't work:
clientside_callback(
    """ () => {
        const gridApi = dash_ag_grid.getApi("grid-flash-cells")
        gridApi.flashCells({ columns: ['c', 'd'] });
        return dash_clientside.no_update
    }""",
    Output("btn-flash-cols", "id"),
    Input("btn-flash-cols", "n_clicks"),
)

getApiAsync Method:

The getApiAsync function is asynchronous and returns a promise. It uses a timeout and will wait for up to 2 minutes for the grid to be ready before it sends an error. (If it takes longer than 2 minutes to load the grid, you should probably be using background callbacks).

Here’s some info from the MDN article:

The async keyword gives you a simpler way to work with asynchronous promise-based code. Adding async at the start of a function makes it an async function.

Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.

This enables you to write code that uses asynchronous functions but looks like synchronous code.

This is the same function as above, but it uses getApiAsync. In this example, even though the callback is triggered when the app starts, it will wait for the grid to render and you will not see the error.

# Flash 2 columns when the app starts using getApiAsync
clientside_callback(
    """async () => {
        const gridApi =  await dash_ag_grid.getApiAsync("grid-flash-cells")
        gridApi.flashCells({ columns: ['c', 'd'] });
        return dash_clientside.no_update
    }""",
    Output("btn-flash-cols", "id"),
    Input("btn-flash-cols", "n_clicks"),
)

Next Steps

  • When migrating to future version of Dash AG Grid, be sure to update the prop names in the flashCells API
  • If you’ve found Dash AG Grid valuable, show your support by starring us on GitHub! :star2:.
  • Head over to Dash AG Grid Examples to see these examples live and for other helpful tutorials

Happy Coding!

9 Likes

This is really helpful. Thank you @AnnMarieW

In the example code of flashing cells, you write javascript inside the clientside callback.

How do you come up with the javascript code? Is it a direct translation from the Flashing Cells chapter in AG Grid (I couldn’t find your code in their example)?

2 Likes

Yes, the examples are a dash version of the examples in the AG Grid doc. The clientside callbacks use the getApiAsync to get the grid’s API. The examples will make more sense by reading all of the information on the AG Grid docs page, including the info on the `flashCells API

2 Likes

Ann- Thank you for the post!

Could you let us know where can we register the “cellFocused” event?
We’re 6 months into the dev and extending the MultiPage example you provided.

We have a master-detail view, but right now only “cellClicked” event can be captured. We’d like to capture “cellFocused” event so users can press up/down key to change the detail view. (instead of a mouse click)

Thank you!

Hi @aydevmo

Yes, the cellFocused event is not a dash prop, but it is accessible using the grid’s API. This is a fairly frequent request because the DataTable has an active_cell prop that triggered a callback when the cell is focused.

See the last example on this page.

@aydevmo

Now that I read you question more carefully, I think the example I linked to above isn’t what you are looking for because it’s how to trigger Dash callback based on the focused cell.

Could you please give me more information on what you are trying to do? Are referring to the AG Grid Enterprise Master/Detail feature? Is there an example in the AG Grid docs for something similar?

Hi @AnnMarieW
Thank you so much for the example!
Yes, it’s exactly what I needed, and it worked like a charm.
Thank you!

1 Like

Hello @aydevmo,

Please note, in a future release, you will be able to add event listeners directly to the grid without needing to use the api. :grin:

Don’t know when that will be released though.

3 Likes