Ag Grid with Multi page apps

Hi There,

Wondering if aggrid works in a multipage app? I have a single page app which updates the ‘rowData’ prop using a callback and works fine, I also have the exact same code but in a multipage app and I get the below error:

A nonexistent object was used in an Output of a Dash callback. The id of this object is g10_table and the property is rowData. The string ids in the current layout are: [url, load, data_memory, position_data_memory_eur, position_data_memory_jpy, eur_traded_memory, jpy_traded_memory, wmr_memory, ccy_dropdown_store, em_bar_memory, dm_bar_memory, date_picker, button_eur, _pages_location, _pages_content, _pages_store, _pages_dummy, table_header_2, em_table, broker_vol_bar]

In the app.layout, I am defining all the necessary attributes of the aggrid table:

dag.AgGrid(
        id='g10_table',
        columnDefs=[
            {"headerName": "Row ID",
             "valueGetter": {"function": "params.node.id"},
             "checkboxSelection": True
             },
            {
                "headerName": "CURRENCY",
                "field": "CCY",
                "filter": True,
                "headerTooltip": "The currency of the trade"
            },
            {
                "headerName": "AVG SPOT RATE",
                "field": "Avg Spot Rate",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Average spot rate traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "ANNUALIZED 1YR (%)",
                "field": "Annualized 1Y (%)",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Annualized cost/benefit of trading the forward rate bias",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 INTER POINTS",
                "field": "WMR4 Inter Points",
                "valueFormatter": "Number(value)",
                "headerTooltip": "London 4pm interpolated points based on the day count",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "NEAR DATE",
                "field": "Near Date",
                "type": "rightAligned",
                "headerTooltip": "Settlement date for the near leg"
            },
            {
                "headerName": "FAR DATE",
                "field": "Far Date",
                "type": "rightAligned",
                "headerTooltip": "Settlement date for the far leg",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 1M DAY COUNT",
                "field": "WMR 1M Days",
                "valueFormatter": "Number(value)",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
                "headerTooltip": "Number of days between spot and forward settlement dates for the benchmark"
            },
            {
                "headerName": "WMR4 MONTH END DATE",
                "field": "WMR4 Eff Date",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Month ENd date used by the benchmark",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
        ],
        columnSize="sizeToFit",
        defaultColDef=defaultColDef,
        dashGridOptions={"undoRedoCellEditing": True,
                         "rowSelection": "single",
                         "tooltipShowDelay": 500
                         },
        style={"width": "100%"}
    )

Then I have a callback to set and generate the rowData

@callback([Output('g10_table', 'rowData'),
           Output('table_header_1', 'children'), Output('dm_bar_memory', 'data')],
          [Input('data_memory', 'data'), Input('wmr_memory', 'data')])
def update_g10_table(data, data_wmr):
    global df_table
    if data is None:
        return None, dcc.Markdown(
            f'''&nbspNo G10 rolls have been captured yet for the selected start date...'''), None

    df_table, df_fx = get_currency_table(data, pd.read_json(data_wmr['wmr_store']))

    if df_table is None:
        return None, dcc.Markdown(
            f'''&nbspNo G10 rolls have been captured yet for the selected start date...'''), None

    display(df_table)

    datasets = {'DM': df_fx.to_json(orient='columns'), 'ALL': df_table.to_json(orient='columns')}

    return df_table.to_dict('records'), dcc.Markdown(
        f'''&nbspG10 rolls summary for the selected start date'''), datasets

I also tripple checked the grid field names vs the dataframe and ensured there is data being printed out.

@AnnMarieW would greatly appreciate your help

1 Like

Hi @nickmuchi

Thanks trying dash-ag-grid and posting this question. And yes, AG Grid definitely works with multi-page apps. The documentation app is a multi-page app made with Dash Pages - and so far it has almost 200 Ag Grid examples

There is not quite enough information in your post to help with a solution. In any multi-page app, all the Inputs, Outputs and States of a callback must exist in the layout when the callback is triggered. Could one of your inputs be triggering the callback when the grid is not in the layout?

And if components are added dynamically, do you have suppress_callbacks_exceptions=True in the app constructor?

app = Dash(__name__, suppress_callback_exceptions=True,)

Thanks, I do have that in my app.py:

app = dash.Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.BOOTSTRAP],
                suppress_callback_exceptions=True,
                background_callback_manager=background_callback_manager)

I do have the below extract in my pagges1.py

import dash
from dash import dcc, html, callback, dash_table, no_update
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from dash.dash_table.Format import Format, Group, Scheme
import dash_ag_grid as dag
from variables import *
from app_functions import get_currency_table, blank_figure

dash.register_page(
    __name__,
    path='/g10-currency-table',
    title='G10 Currency Table',
    name='G10 Currency Table'
)

defaultColDef = {
    "filter": True,
    "resizable": True,
    "sortable": True,
    "editable": False,
    "floatingFilter": True,
    "wrapHeaderText": True,
    "autoHeaderHeight": True
}

layout = html.Div([
    html.Hr(),
    html.H3("G10 Currency Table"),
    html.Hr(),
    html.H3(id='table_header_1', style={'fontSize': 20,
                                        'display': 'inline-block', 'margin-top': '20px',
                                        'padding-left': '0px'}),
    dag.AgGrid(
        id='g10_table',
        columnDefs=[
            {"headerName": "Row ID",
             "valueGetter": {"function": "params.node.id"},
             "checkboxSelection": True
             },
            {
                "headerName": "CURRENCY",
                "field": "CCY",
                "filter": True,
                "headerTooltip": "The currency of the trade"
            },
            {
                "headerName": "USD Notional",
                "field": "USD Notional Traded",
                "filter": True,
                "headerTooltip": "Total amount traded in USD"
            },
            {
                "headerName": "AVG SPOT RATE",
                "field": "Avg Spot Rate",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Average spot rate traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "AVG FORWARD POINTS",
                "field": "Avg Forward Points",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Average forward rate traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "AVERAGE ALL-IN RATE",
                "field": "Avg All-In",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Average  all in (spot rate + forward points) traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "ANNUALIZED 1YR (%)",
                "field": "Annualized 1Y (%)",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Annualized cost/benefit of trading the forward rate bias",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 Spot Rate",
                "field": "WMR4 Spot",
                "valueFormatter": "Number(value)",
                "headerTooltip": "London 4pm spot rate",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 INTER POINTS",
                "field": "WMR4 Inter Points",
                "valueFormatter": "Number(value)",
                "headerTooltip": "London 4pm interpolated points based on the day count",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 1M POINTS",
                "field": "WMR4 1M Points",
                "valueFormatter": "Number(value)",
                "headerTooltip": "London 4pm one month forward points",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 ALL-IN",
                "field": "WMR4 All-in",
                "valueFormatter": "Number(value)",
                "headerTooltip": "London 4pm all in (spot + forward points) rate",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 ANNUALIZED 1YR (%)",
                "field": "WMR4 Annualized 1Y (%)",
                "valueFormatter": "Number(value)",
                "headerTooltip": "London 4pm annualized cost/benefit of trading the forward rate bias",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "NEAR DATE",
                "field": "Near Date",
                "type": "rightAligned",
                "headerTooltip": "Settlement date for the near leg"
            },
            {
                "headerName": "FAR DATE",
                "field": "Far Date",
                "type": "rightAligned",
                "headerTooltip": "Settlement date for the far leg",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "POSITION DAY COUNT",
                "field": "Position Days",
                "type": "rightAligned",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Number of days between spot and forward settlement dates for the fund orders",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 1M DAY COUNT",
                "field": "WMR 1M Days",
                "valueFormatter": "Number(value)",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
                "headerTooltip": "Number of days between spot and forward settlement dates for the benchmark"
            },
            {
                "headerName": "WMR4 MONTH END DATE",
                "field": "WMR4 Eff Date",
                "valueFormatter": "Number(value)",
                "headerTooltip": "Month ENd date used by the benchmark",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },

        ],
        columnSize="sizeToFit",
        defaultColDef=defaultColDef,
        dashGridOptions={"undoRedoCellEditing": True,
                         "rowSelection": "single",
                         "tooltipShowDelay": 500
                         },
        style={"width": "100%"}
    ),
    html.Div(dcc.Graph(id='broker_vol_bar_2', style={'width': '55vw', 'height': '50vh'}, figure=blank_figure()
                       )
             )
]
)

@callback([Output('g10_table', 'rowData'),
           Output('table_header_1', 'children'), Output('dm_bar_memory', 'data')],
          [Input('data_memory', 'data'), Input('wmr_memory', 'data')])
def update_g10_table(data, data_wmr):
    if data is None:
        return no_update, dcc.Markdown(
            f'''&nbspNo G10 rolls have been captured yet for the selected start date...'''), None

    df_table, df_fx = get_currency_table(data, pd.read_json(data_wmr['wmr_store']))

    if df_table is None:
        return no_update, dcc.Markdown(
            f'''&nbspNo G10 rolls have been captured yet for the selected start date...'''), None

    datasets = {'DM': df_fx.to_json(orient='columns'), 'ALL': df_table.to_json(orient='columns')}

    return df_table.to_dict('records'), dcc.Markdown(
        f'''&nbspG10 rolls summary for the selected start date'''), datasets

Ok I inspected the browser and I saw all the columns had the below error:

objectfield: "WMR4 Inter Points
"target: “valueFormatter”
[[Prototype]]: Object
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
‘Blocked a string that AG Grid would evaluate, to prevent XSS attacks. If you really want this, use dangerously_allow_code’

Hello @nickmuchi,

Yes, all your valueFormatter spots need to be used similarly to how you used the valueGetter in the first column.

Not sure I quite follow, I updater my valueFormatter to the below:

            {
                "headerName": "AVG SPOT RATE",
                "field": "Avg Spot Rate",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "Average spot rate traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            }

Still getting the same errors, my value Getter is as below:

{"headerName": "Row ID",
             "valueGetter": {"function": "params.node.id"},
             "checkboxSelection": True
             }```

Did you update all of your valueFormatter’s?

Yes I did for all the columns that are of type float/int

columnDefs=[
            # {"headerName": "Row ID",
            #  "valueGetter": {"function": "params.node.id"},
            #  "checkboxSelection": True
            #  },
            {
                "headerName": "CURRENCY",
                "field": "CCY",
                "filter": True,
                "headerTooltip": "The currency of the trade"
            },
            {
                "headerName": "USD NOTIONAL",
                "field": "USD Notional Traded",
                "filter": True,
                "headerTooltip": "Total amount traded in USD"
            },
            {
                "headerName": "AVG SPOT RATE",
                "field": "Avg Spot Rate",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "Average spot rate traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "AVG FORWARD POINTS",
                "field": "Avg Forward Points",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "Average forward rate traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "AVERAGE ALL-IN RATE",
                "field": "Avg All-In",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "Average  all in (spot rate + forward points) traded for the specific currency",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "ANNUALIZED 1YR (%)",
                "field": "Annualized 1Y (%)",
                "valueFormatter": {"function": "d3.format('.2%')(params.value)"},
                "headerTooltip": "Annualized cost/benefit of trading the forward rate bias",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 Spot Rate",
                "field": "WMR4 Spot",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "London 4pm spot rate",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 INTER POINTS",
                "field": "WMR4 Inter Points",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "London 4pm interpolated points based on the day count",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 1M POINTS",
                "field": "WMR4 1M Points",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "London 4pm one month forward points",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            },
            {
                "headerName": "WMR4 ALL-IN",
                "field": "WMR4 All-in",
                "valueFormatter": {"function": "d3.format('.4f')(params.value)"},
                "headerTooltip": "London 4pm all in (spot + forward points) rate",
            },
            {
                "headerName": "WMR4 ANNUALIZED 1YR (%)",
                "field": "WMR4 Annualized 1Y (%)",
                "valueFormatter": {"function": "d3.format('.2%')(params.value)"},
                "headerTooltip": "London 4pm annualized cost/benefit of trading the forward rate bias",
            },
            {
                "headerName": "NEAR DATE",
                "field": "Near Date",
                "type": "rightAligned",
                "headerTooltip": "Settlement date for the near leg"
            },
            {
                "headerName": "FAR DATE",
                "field": "Far Date",
                "type": "rightAligned",
                "headerTooltip": "Settlement date for the far leg",
            },
            {
                "headerName": "POSITION DAY COUNT",
                "field": "Position Days",
                "type": "rightAligned",
                "valueFormatter": {"function": "d3.format('.0f')(params.value)"},
                "headerTooltip": "Number of days between spot and forward settlement dates for the fund orders"
            },
            {
                "headerName": "WMR4 1M DAY COUNT",
                "field": "WMR 1M Days",
                "valueFormatter": {"function": "d3.format('.0f')(params.value)"},
                "cellRenderer": "agAnimateShowChangeCellRenderer",
                "headerTooltip": "Number of days between spot and forward settlement dates for the benchmark",
            },
            {
                "headerName": "WMR4 MONTH END DATE",
                "field": "WMR4 Eff Date",
                "headerTooltip": "Month End date used by the benchmark",
                "cellRenderer": "agAnimateShowChangeCellRenderer",
            }

What happens when you define rowData=[] in the grid?

still the same, nothing is rendered.

Just checking, but do you have data in the callback?

Ye I exported the dataframe to excel just to make sure and it does have data. This was working when I was using the dash datatable and only stopped working when I switched to ag-grid.

Very interesting.

Can you provide a basic example, along with data, for what you are doing?

Also, what version of AG Grid are you using?

Sure will DM the data to you, I am using the latest version: 2.0.0rc1

@nickmuchi

The default for the style prop in AgGrid is:
style={'height': '400px', 'width': '100%'}

If you set the style prop on the grid level to something else, be sure to set both the height and width.

It look like you only have the width set like this.

dag.AgGrid(
    # other props
    style={"width": "100%"},
)

Wow!!! that fixed it, cannot believe a small miss caused me hours of trying to debug. Thanks you so much for your patience! All working now!

2 Likes

Yeah, that’s something hard to debug - we are looking into ways to fix this in a future release of dash-ag-grid.

1 Like