We are trying to bind a callback to a component during the execution of another callback. However, when the first callback is called it doesn’t set the second callback for some reason.
So, here it is a simple code example:
import dash
import dash_html_components as html
from dash.dependencies import Input, Output, State
app = dash.Dash('test')
app.layout = html.Div([
html.Div([
html.Div([
html.Button(
'Bind callbacks',
id='bt1',
),
html.P(id='result1')
]),
html.Div([
html.Button(
'Button to be bound',
id='bt2',
),
html.P(id='result2')
])
],
style={'display': 'flex'}
)
])
@app.callback(
Output('result1', 'children'),
[Input('bt1', 'n_clicks')]
)
def press_bt1(click):
if click and click % 2 == 1:
bind_callbacks2(app)
return 'Pressed bt1'
return None
def bind_callbacks2(app):
@app.callback(
Output('result2', 'children'),
[Input('bt2', 'n_clicks')]
)
def press_bt2(click):
if click and click % 2 == 1:
print('BT2 click')
return 'Pressed bt2'
return None
app.run_server(debug=True, port=8085)
We have two buttons: bt1 and bt2. We expected that when we click bt1 a new callback, not previously set, would be bound to bt2 from there on. However, although the callback press_bt1
is indeed called and run, it does not set the press_bt2
callback to bt2.
Curiously, if you launch the app, go to http://127.0.0.1:8085/, press bt1, reload the page and press bt2; you’ll see that the second callback press_bt2
has been successfully bound to bt2.
You might be wondering why we want to do this instead of simply binding the callbacks beforehand and using app.config['suppress_callback_exceptions'] = True
. The problem here is that we actually want to do something more complicated: We want to generate new components from another callback (different component depending on different selection chosen by the user) and bind their corresponding bindings too, all in runtime.
You can see a full example of what I mean by this in this repo: https://github.com/Akronix/Dash-test-nesting-callbacks-in-runtime
In that repo you can see that a different button is generated and added to the layout depending on the the RadioItem selection; that works fine, but again the callback for that button is never bound. Also, if you select the first option, then reload the app and select again that first option, you will get a dash.exceptions.CantHaveMultipleOutputs
exception, since the previous binding and the new one are redundantly being attached to the same button (we could control that it’s bound only once, but the problem here is that it isn’t bound in the first click to the RadioItem).