Wrong logic of Intinite row model

Hi. I have large data. I want to reproduce the following logic in my layout:
https://dash.plotly.com/dash-ag-grid/infinite-row-model - Example 2: Equal Pagination Page Size and Large Infinite Block Size

But something is going wrong. Please help me figure it out

I have layout =

dmc.MantineProvider(
    children=html.Div(
        children=[
            dmc.Group(
                children=[
                    dropdown,
                    button,
                ],
            ),
            dmc.LoadingOverlay(
                overlayColor="black",
                loaderProps={"variant": "dots"},
                zIndex=299,
                children=[
                    dag.AgGrid(
                        id='infinite-grid',
                        rowModelType="infinite",
                        columnDefs=generate_default_col_defs(),
                        columnSize="autoSize",  
                        defaultColDef=DEFAULT_COL_DEFS,
                        dashGridOptions=INFINITE_GRID_OPTIONS,
                    ),
                ],
            ),
        ],
    ),
)

dropdown contains the names of the csv files from which we get df. when you click on the button, depending on the selected file, the grid is loaded.

INFINITE_GRID_OPTIONS = {"rowBuffer": 0, "maxBlocksInCache": 2, "cacheBlockSize": 100, "cacheOverflowSize": 2,
                "maxConcurrentDatasourceRequests": 2, "infiniteInitialRowCount": 1, "pagination": True, "paginationPageSize": 100,
                "animateRows": False, 'suppressColumnVirtualisation': True, "rowSelection": "single", "enableCellTextSelection": True}
def generate_default_col_defs(columns=EXPECTED_COLUMNS):
    columnDefs=[
        {'headerName': col, 'field': col, 'suppressSizeToFit': True, 'suppressAutoSize': True, 'minWidth': 220, 'maxWidth': 220, 'width': 220} 
        if col == 'timestamp'
        else {'headerName': col, 'field': col, 'minWidth': 150, 'maxWidth': 150, 'width': 150} for col in columns
    ]
    return columnDefs
def generate_col_defs(columns=EXPECTED_PNL_COLUMNS):
    columnDefs=[
        {'headerName': col, 'field': col, 'minWidth': 150, 'maxWidth': 150, 'width': 150, 
         "valueFormatter": {"function": f"{LOCALE_EN_GB}.format('$,.2f')(params.value)"}} if col in CURRENCY_COLUMNS
        else {'headerName': col, 'field': col, 'suppressSizeToFit': True, 'suppressAutoSize': True, 'minWidth': 220, 'maxWidth': 220, 'width': 220} 
        if col == 'timestamp'
        else {'headerName': col, 'field': col, 'minWidth': 150, 'maxWidth': 150, 'width': 150} for col in columns
    ]
    return columnDefs

callback:

@callback(
    output=[
        Output('infinite-grid', "getRowsResponse"),
    ],
    inputs=[
        Input('load-button', "n_clicks"),
        Input('infinite-grid', "getRowsRequest"),
    ],
    state=[
        State(dropdown', "value"),
    ],
    running=[
        (Output('load-button', "disabled"), True, False),
        # (Output('infinite-grid', "getRowStyle"), {}, GET_ROW_STYLE),
        (Output('infinite-grid', "columnDefs"), generate_default_col_defs(), generate_col_defs()),
    ],
    background=True,
    prevent_initial_call=True
)
def update_layout(n_clicks, request, value):    
    df = get_df(value)   #get df using polars
    if 'load-button' == ctx.triggered_id:
        if request is None:
            # return dash.no_update
            raise PreventUpdate
        else:
            # if value is not None:
            if value:
                partial = df.slice(request["startRow"], request["endRow"]).to_pandas()
                response = {"rowData": partial.to_dict("records"), "rowCount": len(df),}
                return response #GET_ROW_STYLE
            # else: 
            #     return dash.no_update   #, dash.no_update
    else:
        # return dash.no_update
        raise PreventUpdate

if I go to 2 and subsequent grid pages after loading the grid, I get an empty grid, if I select 2 files, the logic that I want to achieve does not work correctly. Please help me figure out and configure the correct operation of the application. Thanks
and how to remove this download when the page is initially loaded:

screen of loading 2 page 0f grid:

and I wanted to make it work, that is, so that when loading data into the grid, the rows would be colored according to a certain parameter in a certain color. maybe it should work in a different way, but I don’t understand how yet:

 (Output('infinite-grid', "getRowStyle"), {}, GET_ROW_STYLE),

Hello @AlesiaSel,

The issue is your callback.

If the grid is the one triggering the event, with the pagination navigation, you are telling it to return no update. :stuck_out_tongue_winking_eye:

You need to adjust your if statement with the ctx triggered.

I didn’t understand it a bit. Do you have any ideas how to fix it? I will be very grateful for your help and assistance. the eye is blurred…

This callback should be rewritten like this:

@callback(
    output=[
        Output('infinite-grid', "getRowsResponse"),
        Output('infinite-grid', 'paginationGoTo')
    ],
    inputs=[
        Input('load-button', "n_clicks"),
        Input('infinite-grid', "getRowsRequest"),
    ],
    state=[
        State(dropdown', "value"),
    ],
    running=[
        (Output('load-button', "disabled"), True, False),
        # (Output('infinite-grid', "getRowStyle"), {}, GET_ROW_STYLE),
        (Output('infinite-grid', "columnDefs"), generate_default_col_defs(), generate_col_defs()),
    ],
    background=True,
    prevent_initial_call=True
)
def update_layout(n_clicks, request, value):
    if value: 
        df = get_df(value)   #get df using polars
        if 'load-button' == ctx.triggered_id:
                partial = df.slice(0, 100).to_pandas()
                response = {"rowData": partial.to_dict("records"), "rowCount": len(df),}
                return response, 0
        elif request:
                partial = df.slice(request["startRow"], request["endRow"]).to_pandas()
                response = {"rowData": partial.to_dict("records"), "rowCount": len(df),}
                return response, no_update
    raise PreventUpdate

The indentation will be adjusted to make it run.

Yes, thanks. Its work good))

But (Output(‘infinite-grid’, “getRowStyle”), {}, GET_ROW_STYLE), not work with this realization. How to fix it, and after loading content of grid second or next pages - get styling rows on page. Its good work only for first page
image
second page -

I have no idea what that is, but my guess is that you need to adjust the running callback.

Right now, that bit is commented out.

if i pass (Output(‘infinite-grid’, “getRowStyle”), {}, GET_ROW_STYLE), in running - this triggers the events that are displayed in the screenshot

I have no way to adjust this for you, as this is not available in the app.

Please create a How to write a Minimal Reproducible Example (MRE) for more assistance.

import dash
from dash import dcc, html, ctx, no_update, callback
import dash_table
from dash.dependencies import Input, Output, State
import pandas as pd
import dash_mantine_components as dmc
import dash_ag_grid as dag
from dash.exceptions import PreventUpdate

app = dash.Dash(__name__)

def generate_default_col_defs(columns):
    columnDefs=[
        {'headerName': col, 'field': col, 'minWidth': 150, 'maxWidth': 150, 'width': 150} for col in columns
    ]
    return columnDefs

app.layout = dmc.MantineProvider(
    children=html.Div(
        children=[
            dmc.Group(
                children=[
                    dcc.Dropdown(
                        id='csv-dropdown',
                        options=[
                            {'label': 'data1.csv', 'value': 'data1.csv'},
                            {'label': 'data2.csv', 'value': 'data2.csv'},
                            {'label': 'data3.csv', 'value': 'data3.csv'}
                        ],
                        placeholder='Choose CSV'
                    ),
                    dmc.Button('Load data', id='load-button', n_clicks=0),
                ],
            ),
            dmc.LoadingOverlay(
                overlayColor="black",
                loaderProps={"variant": "dots"},
                zIndex=299,
                children=[
                    dag.AgGrid(
                        id='infinite-grid',
                        rowModelType="infinite",
                        # columnDefs=generate_default_col_defs(),
                        columnSize="autoSize",  
                        defaultColDef={'resizable': True, 'editable': False, 'sortable': True},
                        dashGridOptions={"rowBuffer": 0, 
                                         "maxBlocksInCache": 2, 
                                         "cacheBlockSize": 100, 
                                         "cacheOverflowSize": 2,
                                         "maxConcurrentDatasourceRequests": 2, 
                                         "infiniteInitialRowCount": 1, 
                                         "pagination": True},
                    ),
                ],
            ),
        ],
    ),
)


@callback(
    output=[
        Output('infinite-grid', "getRowsResponse"),
        Output('infinite-grid', 'paginationGoTo'),
        Output('infinite-grid', "columnDefs"),
    ],
    inputs=[
        Input('load-button', "n_clicks"),
        Input('infinite-grid', "getRowsRequest"),
    ],
    state=[
        State('dropdown', "value"),
    ],
    running=[
        (Output('load-button', "disabled"), True, False),
        # (Output('infinite-grid', "getRowStyle"), {}, GET_ROW_STYLE),
    ],
    background=True,
    prevent_initial_call=True
)
def update_layout(n_clicks, request, value):
    if value: 
        df = pd.read_csv(value)   #get df using polars
        if 'load-button' == ctx.triggered_id:
                partial = df.slice(0, 100).to_pandas()
                response = {"rowData": partial.to_dict("records"), "rowCount": len(df),}
                return response, 0, generate_default_col_defs(df.columns)
        elif request:
                partial = df.slice(request["startRow"], request["endRow"]).to_pandas()
                response = {"rowData": partial.to_dict("records"), "rowCount": len(df),}
                return response, no_update, generate_default_col_defs(df.columns)
    raise PreventUpdate

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

Do you have any base data that I can use?

unfortunately, I cannot provide my own, but on this site you can download 500k lines

hi. was it possible to fix it?

You code had some errors in it that were painful, here is a working example using data from plotly express:

import dash
from dash import dcc, html, ctx, no_update, callback, Input, Output, State
import pandas as pd
import dash_mantine_components as dmc
import dash_ag_grid as dag
from dash.exceptions import PreventUpdate
import plotly.express as px

df = px.data.election()
df2 = px.data.gapminder()

data = {'df': df, 'df2': df2}

app = dash.Dash(__name__)

def generate_default_col_defs(columns):
    columnDefs=[
        {'headerName': col, 'field': col, 'minWidth': 150, 'maxWidth': 150, 'width': 150} for col in columns
    ]
    return columnDefs

app.layout = dmc.MantineProvider(
    children=html.Div(
        children=[
            dmc.Group(
                children=[
                    dcc.Dropdown(
                        id='dropdown',
                        options=[
                            {'label': 'data1.csv', 'value': 'df'},
                            {'label': 'data2.csv', 'value': 'df2'},
                        ],
                        placeholder='Choose CSV'
                    ),
                    dmc.Button('Load data', id='load-button', n_clicks=0),
                ],
            ),
            dmc.LoadingOverlay(
                overlayColor="black",
                loaderProps={"variant": "dots"},
                zIndex=299,
                children=[
                    dag.AgGrid(
                        id='infinite-grid',
                        rowModelType="infinite",
                        # columnDefs=generate_default_col_defs(),
                        columnSize="autoSize",
                        defaultColDef={'resizable': True, 'editable': False, 'sortable': True},
                        dashGridOptions={"rowBuffer": 0,
                                         "maxBlocksInCache": 2,
                                         "cacheBlockSize": 100,
                                         "cacheOverflowSize": 2,
                                         "maxConcurrentDatasourceRequests": 2,
                                         "infiniteInitialRowCount": 1,
                                         "pagination": True},
                    ),
                ],
            ),
        ],
    ),
)


@callback(
    output=[
        Output('infinite-grid', "getRowsResponse"),
        Output('infinite-grid', 'paginationGoTo'),
        Output('infinite-grid', "columnDefs"),
    ],
    inputs=[
        Input('load-button', "n_clicks"),
        Input('infinite-grid', "getRowsRequest"),
    ],
    state=[
        State('dropdown', "value"),
    ],
    running=[
        (Output('load-button', "disabled"), True, False),
        # (Output('infinite-grid', "getRowStyle"), {}, GET_ROW_STYLE),
    ],
    # background=True,
    prevent_initial_call=True
)
def update_layout(n_clicks, request, value):
    if value:
        dff = data.get(value).copy()
        if 'load-button' == ctx.triggered_id:
                partial = dff.to_dict('records')[0:100]
                response = {"rowData": partial, "rowCount": len(dff)}
                return response, 0, generate_default_col_defs(dff.columns)
        elif request:
                partial = dff.to_dict('records')[request["startRow"]:request["endRow"]]
                response = {"rowData": partial, "rowCount": len(dff)}
                return response, no_update, generate_default_col_defs(dff.columns)
    raise PreventUpdate

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

But what about applying to the second df for grid.getRowStyle :

GET_ROW_STYLE = {
    "styleConditions": [
        {
            "condition": "params.data.lifeExp >= 30",
            "style": {"backgroundColor": "rgba(137, 181, 252, 0.22)"},
        },
        {
            "condition": "params.data.lifeExp < 30",
            "style": {"backgroundColor": "rgba(245, 187, 187, 0.52)"}
        },
    ],
    "defaultStyle": {"backgroundColor": "white", "color": "black"}
}

how can I avoid mistakes when navigating to subsequent pages? if in running use - (Output(‘infinite-grid’, “getRowStyle”), {}, GET_ROW_STYLE)
how do I make this work for infinite-grid?

As this info was not provided in the example, I did not test it. You should be able to pass it as an output only when the load_data button is pushed, otherwise leave it alone by returning no update.

achieved the desired result as follows:

GET_ROW_STYLE = {
    "styleConditions": [
        {
            "condition": "params.data && params.data.gross_pnl && params.data.gross_pnl >= 0",
            "style": {"backgroundColor": "rgba(137, 181, 252, 0.22)"},
        },
        {
            "condition": "params.data && params.data.gross_pnl && params.data.gross_pnl < 0",
            "style": {"backgroundColor": "rgba(245, 187, 187, 0.52)"}
        },
    ],
    "defaultStyle": {"backgroundColor": "#23262E"}
    # "defaultStyle": {"backgroundColor": "white", "color": "black"}
}
dag.AgGrid(
                        id='infinite-grid',
                       <all logic>,
                        getRowStyle=GET_ROW_STYLE
                    ),

It works))
Thanks for help

1 Like