Description
Describe your context
The MWE code below does work in this environment:
dash 1.12.0
dash-core-components 1.10.0
dash-html-components 1.0.3
dash-leaflet 0.0.19
dash-renderer 1.4.1
dash-table 4.7.0
The MWE code below does not work in this environment:
dash 1.13.4
dash-core-components 1.10.1
dash-html-components 1.0.3
dash-leaflet 0.0.19
dash-renderer 1.5.1
dash-table 4.8.1
Describe the bug
When there are nested elements with callbacks (e.g. a button inside a div and both have conflicting callbacks), the documented method for differentiating between them (checking ctx.triggered[0]['prop_id'].split('.')[0]
) does not work for Dash 1.13+ (it works with 1.12, though). I am not completely sure whether this was a deliberate change and an update to the documentation is needed or this is a bug.
I have also asked in the community forum first.
Minimal working example code
import dash
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash()
app.layout = html.Div(id="div", style={"background": "yellow", "padding": "200px"}, children=[
html.Button(id="button", children="show infotext"),
html.Div(id="info", children="INFOTEXT", style={"display": "none"})
])
@app.callback(
Output("info", "style"),
[Input("button", "n_clicks"),
Input("div", "n_clicks")])
def test(btn, div):
ctx = dash.callback_context
print("Callback", ctx.triggered)
prop_ids = [x['prop_id'].split('.')[0] for x in ctx.triggered]
if "button" in prop_ids:
print("Action: show")
return {"display": "block"}
else:
print("Action: hide")
return {"display": "none"}
app.run_server(debug=True)
Expected behavior
When the button is pressed, the info box should be displayed. When the background is clicked, the infobox should be hidden.
This code produces the expected behaviour in Dash 1.12. There, the callback is executed once only and prop_ids
is a list of all callbacks ([“button”, “div”]
when clicking on the button and [“div”]
when clicking on the div).
Actual behavior
Since version 1.13, dash behaves differently. The callback function is now executed twice, one time for the button callback and one time for the div. This makes it impossible to figure out which one was actually pressed.
Example output (Dash 1.13) for the above code after clicking one time on the button (the None callback is just the initial callback on load an can be ignored):
Callback [{'prop_id': '.', 'value': None}]
Action: hide
Callback [{'prop_id': 'button.n_clicks', 'value': 1}]
Action: show
Callback [{'prop_id': 'div.n_clicks', 'value': 1}]
Action: hide
So, the button callback is executed, the infobox is displayed, but then the div callback immediately hides the infobox again.