Dash ag-grid: input property when using cellRendererSelector

I have a column within my dash ag-grid where the cell values are either an image or just text. When clicking on an image, I want to open that image within a modal as it is shown here. I am following this example to account for the different cell data types.
However, the callback is not fired when using cellRendererData as input property of the ag-grid.
Here is what I have tried so far:

import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, callback
import pandas as pd
import dash_bootstrap_components as dbc

webb_stephans_quintet = "https://user-images.githubusercontent.com/72614349/179115663-71578706-1ab5-45a5-b809-812c7c3028a7.jpg"
webb_deep_field = "https://user-images.githubusercontent.com/72614349/179115668-2630e3e4-3a9f-4c88-9494-3412e606450a.jpg"
webb_southern_nebula = "This is text"
webb_carina = "https://user-images.githubusercontent.com/72614349/179115673-15eaccb9-d17d-4667-84fb-e0a46fd444e8.jpg"

data_dict = {
    "name": ["Deep Field", "Southern Nebula", "Stephans Quintet", "Carina Nebula"],
    "img": [webb_deep_field, webb_southern_nebula, webb_stephans_quintet, webb_carina],
}

df = pd.DataFrame(data_dict)

columnDefs = [
    {
        "headerName": "Thumbnail or NOT?",
        "field": "img",
        "cellRendererSelector": {'function': 'imageORText(params)'},
        "width": 100,
    },
    {
        "headerName": "Image Name",
        "field": "name",
    },
]

grid = dag.AgGrid(
    id="custom-component-img-grid",
    columnDefs=columnDefs,
    rowData=df.to_dict("records"),
    dashGridOptions={"rowHeight": 100},
    style={"height": 475},
    columnSize="sizeToFit",
)

app = Dash(__name__, external_stylesheets=[dbc.themes.SPACELAB])

app.layout = html.Div(
    [
        dcc.Markdown(
            "Example of cellRenderer with custom Image component. Click on Thumbnail to see full size Image"
        ),
        grid,
        dbc.Modal(id="custom-component-img-modal", size="xl"),
    ]
)


@callback(
    Output("custom-component-img-modal", "is_open"),
    Output("custom-component-img-modal", "children"),
    Input("custom-component-img-grid", "cellRendererData"),
)
def show_change(data):
    if data:
        return True, html.Img(src=data["value"])
    return False, None


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

"""


----------------------
// Place the following in the dashAgGridComponents.js file in the assets folder


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


dagcomponentfuncs.ImgThumbnail = function (props) {
    const {setData, data} = props;

    function onClick() {
        setData(props.value);
    }

    return React.createElement(
        'div',
        {
            style: {
                width: '100%',
                height: '100%',
                display: 'flex',
                alignItems: 'center',
            },
        },
        React.createElement(
            'img',
            {
                onClick,
                style: {width: '100%', height: 'auto'},
                src: props.value,

            },
        )
    );
}

-----------------------------------------
// Place the following in the dashAgGridFunctions.js file in the assets folder

var dagfuncs = window.dashAgGridFunctions = window.dashAgGridFunctions || {};

dagfuncs.imageORText = function(params) {
    var dagcomponentfuncs = window.dashAgGridComponentFunctions;

    const imageDetails = {
        component: dagcomponentfuncs.ImgThumbnail,
    };

    if (params.data && params.data.img && params.data.img.includes('.jpg')) {
        return imageDetails;
    }

    return undefined; // No renderer, let ag-Grid handle default rendering
};

"""

I am using Python 3.12.1, dash 2.18.1 and dash_ag_grid 31.2.0.

Hi @paeger and welcome to the Dash community :slightly_smiling_face:

Thanks for the good minimal example and clear description of the issue! This is an odd one.

I ran this example and got an error in the console: “setData is not a function”, and indeed the prop object that gets passed to the component does not include this function. (The setData function is in the props object for the working example in the docs.) I’m not sure why that’s the case, and this might be a question for @jinnyzor

As a workaround, you can trigger the callback by updating a dcc.Store using setProps:

Change the function to


dagcomponentfuncs.ImgThumbnail = function (props) {

    function onClick() {
        dash_clientside.set_props("store-value", {data: props.value})
    }

   // rest of function


Then change the callback to:


@callback(
    Output("custom-component-img-modal", "is_open"),
    Output("custom-component-img-modal", "children"),
    Input("store-value", "data"),
)
def show_change(data):
    if data:
        return True, html.Img(src=data)
    return False, None



I have an incomplete project recently, and that can fulfill exactly what you want. Below is what my code looks like. for the ag-grid columnDef

def generate_table():
    column_defs = [
        ......
        {
            "headerName": "Map Images",
            "field": "Map Images",
            "cellRenderer": "ImgThumbnail",
            "initialWidth": 120,
            "cellStyle": {"padding": "2px"}, 
        },
        ....
    ]

    return dag.AgGrid(
        id='table',
        className='ag-theme-alpine',
        columnDefs=column_defs,
        rowData=data,
        defaultColDef={"filter": True, "floatingFilter": True,  "wrapHeaderText": True, "autoHeaderHeight": True, "initialWidth": 125 },
        dashGridOptions={"pagination": True, "rowHeight": 90,
                        "loadingOverlayComponent": "CustomLoadingOverlay",
                        "loadingOverlayComponentParams": {
                        "loadingMessage": "One moment please...",
                        "color": "red",
                        },},
        style={'height': '650px', 'width': '100%'}
    )

for the dashAgGridComponentFunctions.js

var dagcomponentfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};
dagcomponentfuncs.ImgThumbnail = function (props) {
    const { setData, data } = props;

    function onClick() {
        setData(props.value);
    }

    return React.createElement(
        'div',
        {
            style: {
                width: '100%',
                height: '100%',
                display: 'flex',
                alignItems: 'center',
            },
        },
        React.createElement(
            'img',
            {
                onClick,
                style: { width: '100%', height: 'auto' },
                src: `data:image/jpeg;base64,${props.value}`,
            },
        )
    );
};

and for the callbacks

# Callback to display images in the modal when an image thumbnail is clicked
@app.callback(
    Output("image-modal", "visible"),
    Output("modal-image-container", "children"),
    Input("table", "cellRendererData"),
    prevent_initial_call=True
)
def show_image_modal(data):
    if not data:
        raise PreventUpdate
    # Check if the data exists and if it was triggered by the correct columns
    if isinstance(data, dict) and data.get("value") and data.get("colId") in ["Map Images", "Trend Images"]:
            images = data['value']
            image_components = [
                fac.AntdImage(
                    src=f"data:image/jpeg;base64,{img}",
                    height=200,
                    preview=True,
                    locale="en-us",
                ) for img in images
            ]
            return True, image_components

    raise PreventUpdate

Untitled

I forgot to mention that I am using the feffery ant components, so for some of the modal actions and image previews you pop out, you don’t have to write additional callbacks.

Hi @AnnMarieW
Thanks a lot for the provided workaround! It works perfect :star_struck:

1 Like