Hi all,
I am experimenting with a basic Dash app that has two pages, each containing a single dropdown. I want these dropdowns to remain synchronized at all times, functioning like ‘Vavien dropdowns’. However, the way multiple pages are handled in Dash seems to make this task quite challenging. Below is my minimal working example:
app.py:
import dash
from dash import Dash, html, dcc
app = Dash(__name__, suppress_callback_exceptions=True, use_pages=True)
server = app.server
app.layout = html.Div([
html.H1('Multi-page app with Dash Pages'),
html.Div([
html.Div(
dcc.Link(f"{page['name']}",
href=page["relative_path"])
) for page in dash.page_registry.values()
]),
dash.page_container,
dcc.Store(id='between-pages', storage_type='memory'), # Shared state store
])
if __name__ == '__main__':
app.run(debug=True)
dropdown1.py:
from dash import Input, Output, dcc, html, callback, register_page, State, no_update
from dash import callback_context as ctx
from dash.exceptions import PreventUpdate
register_page(__name__, path="/", name="Dropdown1")
dropdown_options = ['A', 'B', 'C']
layout = html.Div([
dcc.Dropdown(
id='dropdown1',
persistence=True,
persistence_type='session',
options=[
{'label': i, 'value': i}
for i in dropdown_options],
)
])
@callback(
[Output('dropdown1', 'value'),
Output('between-pages', 'data')
],
[Input('dropdown1', 'value'),
Input('between-pages', 'data')
],
)
def sync_dropdown_and_store(dd1_value, store_value):
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
value = store_value if trigger_id == "between-pages" else dd1_value
return value, value
dropdown2.py:
from dash import Input, Output, dcc, html, callback, register_page, State
from dash import callback_context as ctx
register_page(__name__, name="Dropdown2")
dropdown_options = ['A', 'B', 'C']
layout = html.Div([
dcc.Dropdown(
id='dropdown2',
persistence=True,
persistence_type='session',
options=[
{'label': i, 'value': i}
for i in dropdown_options],
)
])
# Initialize dropdown value from shared state
@callback(
Output('dropdown2', 'value'),
[Input('between-pages', 'data')],
State('dropdown2', 'value')
)
def initialize_dropdown2(store_value, dd2_value):
if store_value is not None and store_value != dd2_value:
return store_value
return dd2_value
@callback(
[Output('dropdown2', 'value', allow_duplicate=True),
Output('between-pages', 'data', allow_duplicate=True)
],
[Input('dropdown2', 'value'),
Input('between-pages', 'data')
],
prevent_initial_call=True
)
def sync_dropdown_and_store(dd2_value, store_value):
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
value = store_value if trigger_id == "between-pages" else dd2_value
return value, value
Additionally, I am encountering an issue with suppress_callback_exceptions
. Despite setting it to True
, I am still receiving the error: “A nonexistent object was used in an Input of a Dash callback…” for the component already present in the layout at the app’s initialization.
I would appreciate any advice or solutions to make my minimal example work as intended.
Looking forward to your suggestions!