Inter-pages callback in MultiPage App

Hello again!))

I have a multipage app.

  1. Page http://localhost:8050/events/ where there is Ag Grid table
  2. Page http://localhost:8050/event/<event_id> where there is a Button for closing event.

I want to send an update transaction to AgGrid table when clicking “Close event” button.

But it turned out that callbacks can interact only with elements on the same page.
I tried to use proxy-Store placed in the main layout but it didn’t work out.

@dashapp.callback(
    Output('events-table', 'rowTransaction'),
    Input('globstore', 'data'),
    prevent_initial_call=True,
)
def update_table(transaction):
    return {'update': transaction}

This callback can’t be triggered regardless of it is placed in the events.py or main.py.
However the following callback on the event/<event_id> page works

@callback(
    Output('globstore', 'data'),
    Input('is-incident-btn', 'n_clicks'),
    State('event-id', 'data'),
    prevent_initial_call=True
)
def write_transaction(clicks, event_id):
    if not clicks:
        raise PreventUpdate
    transaction = [{'_id': event_id, 'is_incident': True}]
    return transaction

Is there any solution?

Hello @yukian72,

Can you provide a full example code to demonstrate what you’ve tried?

main.py
def init_app(server: Flask, path: str):

    dashapp = Dash(__name__, server=server, external_stylesheets=dmc.styles.ALL,
                   url_base_pathname=path,
                   use_pages=True,
                   suppress_callback_exceptions=True)

    store = dcc.Store(id='globstore', data=1)

    layout = dmc.AppShell(
        children=[
            
            dmc.AppShellHeader(),

            dmc.AppShellNavbar(
                children=[
                    dmc.NavLink(
                        label="События",
                        leftSection=DashIconify(icon='material-symbols:e911-emergency', height=24),
                        href='/events/',
                    )
                ],
                p='md'
            ),

            dmc.AppShellMain([store, page_container]),

        ],
    )


    dashapp.layout = dmc.MantineProvider(layout, forceColorScheme='dark')


    return dashapp.server


pages/events.py
dash.register_page(__name__)

...

table = dag.AgGrid(
    id="events-table",
    className='ag-theme-quartz-dark',
    dashGridOptions={'pagination': True,
                     'paginationPageSize': 30,
                     'domLayout': 'autoHeight',
                     'rowSelection': 'multiple',
                     'suppressRowClickSelection': True},
    getRowId='params.data._id',
    rowClassRules=rowClassRules,
    style={'height': None},
    eventListeners={'rowDataUpdated': ['refreshCells(params, setGridProps)']}
)

...

@callback(
    Output('events-table', 'rowTransaction', allow_duplicate=True),
    Input('globstore', 'data'),
    prevent_initial_call=True,
)
def update_table(transaction):
    return {'update': [transaction]}

layout = html.Div(
    children=[
        dmc.Container(table, fluid=True, style={"marginTop": 7}),
    ]
)

Column ‘description’ is defined as follows

{
    'field': 'description',
    'headerName': 'Description',
    'flex': 1,
    'cellRenderer': 'eventLink',
    'wrapText': True,
}

The links in this column have format ‘https://localhost/event/<event_id>’

pages/event.py
dash.register_page(__name__, path_template='/event/<event_id>')

...

@callback(
    Output('globstore', 'data'),
    Input('close-event-btn', 'n_clicks'),
    State('event-id', 'data'),
    State('globstore', 'data'),
    prevent_initial_call=True
)
def write_transaction(clicks, event_id, data):
    if not clicks:
        raise PreventUpdate
    transaction = {'_id': event_id, 'is_closed': True}
    return transaction

My notes:

  1. Callback write_transaction is working. It writes transaction details in globstore
  2. Callback update_table doesn’t trigger. ONE TIME during many runs is was triggered. I don’t know how)) But It didn’t happen anymore. And I think that time callback write_transaction wasn’t triggered.
  3. I’ve found Passing data between pages via a dcc.Store · Issue #94 · plotly/dash-labs · GitHub
  4. I use Flask in my project so that’s the reason why my dashapp is defined inside INIT_APP function.

Thanks for the code, there are still some blanks, check out here to help with troubleshooting your issue as there is too much for me to try to figure out.

I prepared a toy project for experiments GitHub - Y0shiT0shi/dash-inter-pages-callbacks

I decided to use DashExtensions Pages to share one AgGrid between different app pages. And in general it even partially works. Steps to check:

  1. Open app
  2. Go to the second page and have a look at the table
  3. Click button → new row added
  4. Go to the first page and click the button there
  5. Go to the second page again → new row from clicking the button on the first page added as well

But.. if you do that:

  1. Open app
  2. Open the second page in a new browser tab
  3. Click button → new row added
  4. Switch to the first browser tab with the first page opened → click the button → go to the second page in the first browser tab → ONLY row from this step has been added

It looks like we have two different tables despite the fact that we have only one AgGrid component.
I tried to play with persistence property of AgGrid but had no success.
I want to open events in separate tabs so this behavior is a problem.

Yes, the data is not shared across tabs, that would take using something linking the two together. You will need something like websockets, or a db to store the info against a user.

If you want to use that, then you need to do a little more work too.

Here is an example of using a global variable:

This is not the recommended path, but just demoing how you’d perform it.

@jinnyzor hello there))

I managed to sync browser tabs using websockets in Dash
Thank you very much for the idea!!

1 Like