Dash AG Grid - Header Click Event

Hi,

I am using dash-ag-grid and would like to trigger a Dash callback when a column header is clicked.

The goal is to react to a header click itself (not sorting, filtering, or cell clicks), for example to open a modal or update another component.

I checked the documentation and tried:

  • cellClicked

  • sortChanged

  • columnState

but none of these fire on a simple header click.

Minimal example

import dash
from dash import html, Output, Input
import dash_ag_grid as dag

app = dash.Dash(__name__)

columnDefs = [
    {"headerName": "A", "field": "a"},
    {"headerName": "B", "field": "b"},
]

rowData = [
    {"a": 1, "b": 2},
    {"a": 3, "b": 4},
]

app.layout = html.Div([
    dag.AgGrid(
        id="grid",
        columnDefs=columnDefs,
        rowData=rowData,
        dashGridOptions={
            "sortable": True,
            "filter": True,
        }
    ),
    html.Div(id="output")
])

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

Question

Is there a supported way in dash-ag-grid to:

  • detect a column header click, or

  • attach a custom JavaScript header event and emit it to Dash?

If not currently supported, what would be the recommended workaround?

Thanks in advance!

Hello @Milan

This should offer some guidance.

Essentially you can create your own header component and capture clicks on it.

2 Likes

Hi @jinnyzor ,

Thank you for response. It was really helpful.

Idea that I wanted to implement is to have filter appear when clicking on column header. Also, I wanted to change folor of header when filter is applied.

Here is prototype that I made using your suggestions:

python file:

"""
2
"""

import dash
from dash import Dash, html, dcc, Input, Output, State
import dash_ag_grid as dag
import dash_mantine_components as dmc


app = Dash()


columnDefs = [
    {"headerName": "A", "field": "a"},
    {"headerName": "B", "field": "b"},
]

rowData = [
    {"a": 1, "b": 2},
    {"a": 3, "b": 4},
]


app.layout = dmc.MantineProvider(
    [
        dcc.Store(id="store-button"),
        dag.AgGrid(
            id="column-definition-ID",
            rowData=rowData,
            dashGridOptions={
                "sortable": False,
                "filter": False,
            },
            columnDefs=[
                {
                    "field": "a",
                    "headerName": "Column Name A",
                    "headerComponent": "ButtonHeader",
                    "headerComponentParams": {
                        "id": "button-header-a",
                        "className": "custom-button-class",
                    },
                },
                {
                    "field": "b",
                    "headerName": "Column Name B",
                    "headerComponent": "ButtonHeader",
                    "headerComponentParams": {
                        "id": "button-header-b",
                        "className": "custom-button-class",
                    },
                    "style": {
                        "backgroundColor": "transparent",
                        "border": "none",
                        "cursor": "pointer",
                        "padding": "5px",
                    },
                },
            ],
            columnSize="sizeToFit",
        ),
        html.Div(id="container"),
        html.Div(id="color-trigger", style={"display": "none"}),
        html.Button(id="new-btn", children=["Click me to apply color"], hidden=True),
        dmc.Modal(
            id="modal-test",
            children=[
                dmc.Text("Modal Test"),
                html.Button(id="new-btn-backend", children=["Apply Filters"]),
            ],
        ),
    ]
)


# Perform actual change of color
app.clientside_callback(
    """
    function(n_clicks, data) {
        if (data && data.id) {
            const button = document.getElementById(data.id);
            if (button) {
                // Check current background color
                const currentBg = button.style.backgroundColor;

                // Toggle: if green, remove color; if not green, make it green
                if (currentBg === 'rgb(76, 175, 80)' || currentBg === '#4CAF50') {
                    // Reset to default (no color)
                    button.style.backgroundColor = 'transparent';
                    button.style.color = '';
                } else {
                    // Make it green
                    button.style.backgroundColor = '#4CAF50';
                    button.style.color = 'white';
                }
            }
        }
        return '';
    }
    """,
    Output("color-trigger", "children"),
    Input('new-btn', 'n_clicks'),
    State("store-button", "data")

)


# Regular callback to print and display info
# --> This should open modal
@app.callback(
    Output("container", "children"),
    Output('modal-test', 'opened'),
    Input("store-button", "data"),
)
def update(data):

    print(data)

    if data:
        button_id = data.get("id")
        timestamp = data.get("timestamp")

        # Print in terminal
        print(f"Button ID: {button_id}")
        print(f"Timestamp: {timestamp}")

        print("************")

        # Display in UI
        return html.Div(
            [html.P(f"Last clicked button: {button_id}"), html.P(f"Time: {timestamp}")]
        ), True
    print('---------------')

    return "No button clicked yet", False


# Backend Button
# --> Used for coloring header column
@app.callback(
    Output("new-btn", "n_clicks"),
    Input("new-btn-backend", "n_clicks"),
    prevent_initial_calback=True
)
def update(n_clicks):

    if n_clicks:

        return n_clicks + 1

    else:
        return dash.no_update



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


dashAgGridComponentFunctions.js file":

var dagcomponentfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};

dagcomponentfuncs.ButtonHeader = function (props) {
    return React.createElement(
        'button',
        {
            id: props.id || 'button-header', // Add ID here
            onClick: () => {
                // newData = JSON.stringify(new Date())

                newData = {
                    id: props.id,
                    timestamp: new Date().toISOString(),
                    field: props.column?.colId  // optionally include the field name
                }
                dash_clientside.set_props("store-button", {data: newData})

                // dash_clientside.set_props("store-button", {data: newData})
            },
            className: props.className,
            // style: props.style,
            style: {
                backgroundColor: 'transparent',
                border: 'none',
                cursor: 'pointer',
                padding: '5px 10px',
                color: 'inherit',
                font: 'inherit',
                ...props.style  // Merge with any styles passed from props
            },
        },
        props.displayName)
}

1 Like