plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.26k stars 2.05k forks source link

Persistence does not work with Circular Callbacks #1856

Open RunQi-Han opened 2 years ago

RunQi-Han commented 2 years ago

persistence does not work in circular callbacks. See the example below: This example is based on: "Synchronizing Two Checklists" from https://dash.plotly.com/advanced-callbacks

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(name, external_stylesheets=external_stylesheets)

options = [ {"label": "New York City", "value": "NYC"}, {"label": "Montréal", "value": "MTL"}, {"label": "San Francisco", "value": "SF"}, ] all_cities = [option["value"] for option in options]

app.layout = html.Div( [ dcc.Checklist( id="all-checklist", options=[{"label": "All", "value": "All"}], value=[], labelStyle={"display": "inline-block"}, persistence=True, ), dcc.Checklist( id="city-checklist", options=options, value=[], labelStyle={"display": "inline-block"}, persistence=True, ), ] ) @app.callback( Output("city-checklist", "value"), Output("all-checklist", "value"), Input("city-checklist", "value"), Input("all-checklist", "value"), ) def sync_checklists(cities_selected, all_selected): ctx = dash.callback_context input_id = ctx.triggered[0]["prop_id"].split(".")[0] if input_id == "city-checklist": all_selected = ["All"] if set(cities_selected) == set(all_cities) else [] else: cities_selected = all_cities if all_selected else [] return cities_selected, all_selected

if name == "main": app.run_server(debug=True)


A current workaround is to use `dcc.Store` to store the selections:

import dash from dash.dependencies import Input, Output, State import dash_core_components as dcc import dash_html_components as html

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(name, external_stylesheets=external_stylesheets)

options = [ {"label": "New York City", "value": "NYC"}, {"label": "Montréal", "value": "MTL"}, {"label": "San Francisco", "value": "SF"}, ] all_cities = [option["value"] for option in options]

app.layout = html.Div( [ dcc.Checklist( id="all-checklist", options=[{"label": "All", "value": "All"}], value=[], labelStyle={"display": "inline-block"}, persistence=True, ), dcc.Checklist( id="city-checklist", options=options, value=[], labelStyle={"display": "inline-block"}, persistence=True, ), html.Div(id='dummy-trigger'), dcc.Store(id='temp-store',storage_type="local") ] ) @app.callback( Output("city-checklist", "value"), Output("all-checklist", "value"), Output('temp-store',"data"), Input("city-checklist", "value"), Input("all-checklist", "value"), Input('dummy-trigger',"children"), State('temp-store',"data"), ) def sync_checklists(cities_selected, all_selected, dummy_trigger, stored_fitlers): ctx = dash.callback_context input_id = ctx.triggered[0]["prop_id"].split(".")[0] if input_id == "city-checklist": all_selected = ["All"] if set(cities_selected) == set(all_cities) else [] elif input_id == "all-checklist": cities_selected = all_cities if all_selected else [] else: cities_selected,all_selected = stored_fitlers return cities_selected, all_selected,[cities_selected, all_selected]

if name == "main": app.run_server(debug=True)

astrowonk commented 2 years ago

I made a very simple reproduction in a repo here. Set value from a button, persistence fails.

I had to hack a weird workaround with dcc.Store to get around this so I hope there's some hope for progress?

https://github.com/astrowonk/demo_dash_bug