plotly/dash-ag-grid

Custom Detail Cell with Grid doesn't update

Closed this issue · 6 comments

ctdunc commented

Hi! I am trying to create a custom master/detail cell renderer as shown here using dashAgGridComponentFunctions.js. I can get the table and custom elements to appear, but have been unable to register the grid so that the getDetailRequest callback fires correctly in Dash. My cell renderer is shown below.

Is this something that's currently supported/will be supported in the future? Thank you!

var dagcomponentfuncs = (window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {});
console.log(window.dash_ag_grid.AgGrid)
dagcomponentfuncs.DetailCellRenderer = function (params) {
    console.log(params)
    const rowId = params.node.id;
    React.useEffect( () =>{
        return () => {
            params.api.removeDetailGridInfo(rowId);
        };
    });

    const colDefs = params.detailGridOptions.columnDefs;

    const onGridReady = (p) => {
        const gridInfo = {
            id: rowId,
            api: p.api,
            columnApi: p.columnApi
        }
        params.api.addDetailGridInfo(rowId, gridInfo);
    };

    return React.createElement(
        'div', {className:"full-width-panel", rowId: rowId},
        React.createElement('b', {}, "Fund:" + params.data.Fund),
        React.createElement(window.dash_ag_grid.AgGrid, {
                                          className: "fill-width-grid ag-theme-alpine",
                                          columnDefs: colDefs,
                                          onGridReady: onGridReady,
                                          suppressCallback: params.suppressCallback,
                                         }, "") 
    )
};
BSd3v commented

Hello @ctdunc,

Can you please post more of an example of how you are trying to use it?

Also, if you could please post this question on the plotly forums?

ctdunc commented

Thanks for your comment. Having trouble signing up for the plotly forums (activation email doesn't seem to be sending), but I'll post this there as soon as I figure that out.

In the meantime, I'm trying to use master/detail in a dash page where I want to render some extra information about the detail grid before the grid is displayed. The relevant python code is below

@callback(Output("ag-overview-table", "getDetailResponse"),
          Input("ag-overview-table", "getDetailRequest"),
          State("date-picker-single", "date"),
          prevent_initial_call=True)
def handle_request(req, date):
    print("request!")
    ticker = req["data"]["Fund"]
    data = db_wrapper.get_snowflake_fund_data(first_date = date,
                                            fund_list = [ticker],
                                            equity_only=False)
    if data is None:
        # if you return none, the detail grid will be stuck on loading.
        return []
    return data.drop(columns=detail_grid_drop).to_dict('records')

def layout_load():
    # last business day so that we don't just get empty data
    today = (datetime.date.today() - 1*BDay()).strftime("%Y-%m-%d")
    return [html.Div([dcc.DatePickerSingle(id='date-picker-single', 
                                initial_visible_month=today,
                                date=today,
                                max_date_allowed=today),
            html.Button("Show Short View", id="btn-toggle-detail" ),
            html.Button("Export as CSV", id="btn-export-csv")]),
            html.Div(dag.AgGrid(id='ag-overview-table', 
                            rowData=rowData, # this works, it's just redacted
                            columnDefs=master_grid_columns,
                            defaultColDef={"sortable": True, "filter": True, "resizable": True},
                            dashGridOptions={"pagination": True,
                                             "paginationAutoPageSize": True,
                                             "sideBar": True,
                                             "rowGroupPanelShow": 'always',
                                             "detailRowHeight": 600, 
                                             # This is the issue -- opening the detail row no longer 
                                             # triggers getDetailRequest with a custom detailCellRenderer,
                                             # but the grid and custom content still appear
                                             "detailCellRenderer": "DetailCellRenderer"
                                             },
                            style={"height": "100%"},
                            csvExportParams={"fileName": "export.csv"},
                            enableEnterpriseModules=True,
                            licenseKey=licenseKey,
                            masterDetail=True,
                            detailCellRendererParams = {"detailGridOptions": {"columnDefs":detail_grid_columns,
                                                                              "sideBar": True,
                                                                              "pagination": True,
                                                                              },
                                                        "suppressCallback": False,
                                                        "detailColName": "Fund"}),
                style={"height": "95vh"})
    ]
layout = layout_load()

hi @ctdunc
Thank you for posting this. Did the forum email end up in your spam box by any chance. Are you able to share with me the email address you used to sign up? My email is: adam@plot.ly

Thanks,
adam
Plotly Community Manager

BSd3v commented

Alright, to utilize your own detail renderer with a callback, you'll need to perform the fetch for the rowData yourself.

I have an example of creating your own endpoints for data on the forums.

I'll make a working version so we can have an example of this. :)

BSd3v commented

Here is a working example of how to cater back your own detail request, similar to how they load their data in examples:

from dash import Dash, html, Input, Output, callback
import dash_ag_grid as dag
import flask
import json

app = Dash(__name__)

@app.server.route('/fetchData', methods=['POST'])
def caterData():
    print(flask.request.json)
    return json.dumps(flask.request.json['cities'])

masterColumnDefs = [
    {
        "headerName": "Country",
        "field": "country",
        "cellRenderer": "agGroupCellRenderer",
    },
    {"headerName": "Region", "field": "region"},
    {"headerName": "Population", "field": "population"},
]

detailColumnDefs = [
    {"headerName": "City", "field": "city"},
    {"headerName": "Pop. (City proper)", "field": "population_city"},
    {"headerName": "Pop. (Metro area)", "field": "population_metro"},
]

rowData = [
    {
        "country": "China",
        "region": "Asia",
        "population": 1411778724,
        "cities": [
            {"city": "Shanghai", "population_city": 24870895, "population_metro": "NA"},
            {"city": "Beijing", "population_city": 21893095, "population_metro": "NA"},
            {
                "city": "Chongqing",
                "population_city": 32054159,
                "population_metro": "NA",
            },
        ],
    },
    {
        "country": "India",
        "region": "Asia",
        "population": 1383524897,
        "cities": [
            {
                "city": "Delhi",
                "population_city": 16753235,
                "population_metro": 29000000,
            },
            {
                "city": "Mumbai",
                "population_city": 12478447,
                "population_metro": 24400000,
            },
            {
                "city": "Kolkata",
                "population_city": 4496694,
                "population_metro": 14035959,
            },
        ],
    },
    {
        "country": "United States",
        "region": "Americas",
        "population": 332593407,
        "cities": [
            {
                "city": "New York",
                "population_city": 8398748,
                "population_metro": 19303808,
            },
            {
                "city": "Los Angeles",
                "population_city": 3990456,
                "population_metro": 13291486,
            },
            {
                "city": "Chicago",
                "population_city": 2746388,
                "population_metro": 9618502,
            },
        ],
    },
    {
        "country": "Indonesia",
        "region": "Asia",
        "population": 271350000,
        "cities": [
            {
                "city": "Jakarta",
                "population_city": 10154134,
                "population_metro": 33430285,
            },
        ],
    },
]


app.layout = html.Div(
    [
        dag.AgGrid(
            enableEnterpriseModules=True,
            # licenseKey=os.environ["AGGRID_ENTERPRISE"],
            id="nested-grid-detail-table-request-2",
            columnDefs=masterColumnDefs,
            rowData=rowData,
            columnSize="sizeToFit",
            masterDetail=True,
            detailCellRendererParams={
                "detailGridOptions": {
                    "columnDefs": detailColumnDefs,
                },
                "suppressCallback": False,
            },
            dashGridOptions={"detailRowAutoHeight": True, 'detailCellRenderer': 'DetailCellRenderer'},
        ),
    ]
)

#
# @callback(
#     Output("nested-grid-detail-table-request-2", "getDetailResponse"),
#     Input("nested-grid-detail-table-request-2", "getDetailRequest"),
#     prevent_initial_call=True,
# )
# def handle_request(request):
#     time.sleep(1)
#     return request["data"]["cities"]


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

altered js file:

dagcomponentfuncs.DetailCellRenderer = function (params) {
    const rowId = params.node.id;
    React.useEffect( () =>{
        return () => {
            params.api.removeDetailGridInfo(rowId);
        };
    });

    const colDefs = params.detailGridOptions.columnDefs;

    const onGridReady = (p) => {
        const gridInfo = {
            id: rowId,
            api: p.api,
            columnApi: p.columnApi
        }
        fetch('../fetchData', {method: 'POST', body: JSON.stringify(params.data),
         headers: {'content-type': 'application/json'}})
          .then((resp) => resp.json())
          .then((data) => p.api.setRowData(data));
        params.api.addDetailGridInfo(rowId, gridInfo);
    };

    return React.createElement(
        'div', {className:"full-width-panel", rowId: rowId},
        [
        React.createElement(window.dash_ag_grid.AgGrid, {
                                          className: "fill-width-grid ag-theme-alpine",
                                          columnDefs: colDefs,
                                          onGridReady: onGridReady,
                                          suppressCallback: params.suppressCallback,
                                         }, "")]
    )
};
ctdunc commented

Thank you! Closing this as answered :)