I have a Dash app where user in one page, data.py, adds some data, and later selected rows can be viewed and removed on another page, grid.py. The user should be able to later get back to data.py and add some more data.
The problem: data is not persisted between the visits to the grid.py. How can I achieved that? I tried setting persistence property, but that didn't get me anywhere. existing_data in grid.py is always None. When I use a single-page app, similar code works.
Here's my minimal reproducible example:
app.py
from dash import html, dcc
import dash
app = dash.Dash(__name__, use_pages=True)
app.layout = html.Div(
[
dcc.Store(id="store", data={}),
html.H1("Multi Page App Demo: Sharing data between pages"),
html.Div(
[
html.Div(
dcc.Link(f"{page['name']}", href=page["path"]),
)
for page in dash.page_registry.values()
]
),
html.Hr(),
dash.page_container,
]
)
if __name__ == "__main__":
app.run_server(debug=True)
data.py
from dash import html, Input, Output, callback, register_page
from dash.exceptions import PreventUpdate
import random
register_page(__name__, path="/")
layout = html.Div(
[
html.H3("Data input"),
html.Button("Add row", id="button_id"),
html.Br(),
html.Div(id="my-output"),
]
)
@callback(
[Output("store", "data"), Output("my-output", "children")],
Input("button_id", "n_clicks"),
prevent_initial_call=True
)
def add_data(n_clicks):
if n_clicks:
new_data = [{"col1": "New row", "col2": random.randint(0, 1000)}]
return new_data, html.Pre(str(new_data))
else:
raise PreventUpdate
grid.py
from dash import html, dash_table, Input, Output, callback, register_page, State
register_page(__name__)
layout = html.Div([
html.H3("Data tables"),
dash_table.DataTable(
id="table",
row_deletable=True,
column_selectable="single",
page_size=5,
persistence=True,
persisted_props=[
"data",
"columns.name",
"filter_query",
"hidden_columns",
"page_current",
"selected_columns",
"selected_rows",
"sort_by",
],
),
])
@callback(
Output("table", "data"),
Input("store", "data"),
State("table", "data"),
)
def update(new_data, existing_data):
if existing_data is not None:
return existing_data + new_data
else:
return new_data
I have figured out two ways of doing it:
Storecomponent.I am going to share both for completeness. One reason for having (2) is that I do not fully understand why (1) works, as to my knowledge it introduces a circular dependency. The latter should throw an exception or, worse, result in an infinite callback cycle, which it doesn't. That's smart on behalf of
Dash, but I can't be certain it's going to work always - it's not documented.Shared
Storecomponentapp.py:
pages/data.py
pages/grid.py:
Using a persistent component feature from
dash_extensions.pagesapp.py:
pages/components.py
pages/data.py
pages/grid.py