Hi!
In my app I have a dcc.Tabs
component with multiple dcc.Tab
which each have multiple different inputs of different types (dropdowns, text inputs, etc.).
Outside of the tabs is a button, which takes the inputs of the current tab, does some operations with it and sends it to a store.
However, since the inputs of each tab is unique and the operations to treat each set of inputs in also unique to a tab, I have created a callback for each tab.
The problem is that each of these callback share the same input (the button outside the tabs) and the same output (the store).
So, for n tabs and n callbacks, I get a n-1 Duplicate callback outputs
errors for the store component.
I get that the Dash is preventing me from executing identical callbacks, but I made sure in the callbacks to include a state which ensure that if the wrong callback is called, the callback returns no_update
.
Here a simplified example of my code:
from dash import Dash, Output, Input, State, dcc, no_update
import dash_bootstrap_components as dbc
app = Dash(
__name__,
suppress_callback_exceptions=True,
prevent_initial_callbacks="initial_duplicate"
)
app.layout = dbc.Container([
dcc.Tabs([
dcc.Tab(value="tab1", children=[dcc.Input(id="inp1")]),
dcc.Tab(value="tab2", children=[dcc.Input(id="inp2")]),
dcc.Tab(value="tab3", children=[dcc.Input(id="inp3")]),
], id="tabs"),
dbc.Button("Confirm", id="confirm_btn"),
dcc.Store(id="store")
])
@app.callback(Output("store", "data", allow_duplicate=True),
Input("confirm_btn", "n_clicks"),
State("inp1", "value"),
State("tabs", "value"),
prevent_initial_call=True)
def store_input_1_value(n_clicks, value1, tab):
if not (n_clicks and value1 and tab == "tab1"):
return no_update
# Do some operatations with value1 and return it to the store.
return value1
@app.callback(Output("store", "data", allow_duplicate=True),
Input("confirm_btn", "n_clicks"),
State("inp2", "value"),
State("tabs", "value"),
prevent_initial_call=True)
def store_input_2_value(n_clicks, value2, tab):
if not (n_clicks and value2 and tab == "tab2"):
return no_update
# Do some different operatations with value3 and return it to the store.
return value2
@app.callback(Output("store", "data", allow_duplicate=True),
Input("confirm_btn", "n_clicks"),
State("inp3", "value"),
State("tabs", "value"),
prevent_initial_call=True)
def store_input_3_value(n_clicks, value3, tab):
if not (n_clicks and value3 and tab == "tab2"):
return no_update
# Do some very different operatations with value2 and return it to the store.
return value3
Is there a workaround to this?
I have managed to make it work with up to 3 callbacks by using n_clicks_timestamp
instead of n_clicks
on one Input
and also not using allow_duplicate=True
on the last callback, like such:
@app.callback(Output("store", "data", allow_duplicate=True),
Input("confirm_btn", "n_clicks"),
State("inp1", "value"),
State("tabs", "value"),
prevent_initial_call=True)
def store_input_1_value(n_clicks, value1, tab):
...
@app.callback(Output("store", "data", allow_duplicate=True),
Input("confirm_btn", "n_clicks_timestamp"),
State("inp2", "value"),
State("tabs", "value"),
prevent_initial_call=True)
def store_input_2_value(n_clicks, value2, tab):
...
@app.callback(Output("store", "data"),
Input("confirm_btn", "n_clicks"),
State("inp3", "value"),
State("tabs", "value"),
prevent_initial_call=True)
def store_input_3_value(n_clicks, value3, tab):
...
but I would need to have more than 3 tabs/callbacks.
And merging my callbacks together and using ctx
is not really an option since the callbacks are very long and already have 6-7 State
each.
I believe I could use pattern-matching to simplify all the State
, but it still would be pretty complicated.
Thank you,
Thomas