When having a pattern-matching callback with two different types as input and output like
@app.callback(
Output({“type”: “input”, “index”: MATCH}, “value”),
Input({“type”: “button”, “index”: MATCH}, “n_clicks”),
)
def addButton …
the callback fires for every index, every time an id of type button is added to the layout. This leads to issues, because the button 1 callback fires not only if button 1 is clicked, but also if e.g. button 5 is added to the layout.
As a workaround I have added a dcc.Store called noUpdate to distinguish between user clicks and initial calls because a button was added. I check callback_context.triggered_prop_ids if the store is present and if it is, PreventUpdate is raised.
from dash import (MATCH,Dash,Input,Output,Patch,callback_context,html,dcc,no_update)from dash.exceptions import PreventUpdate
app = Dash()
app.layout = html.Div(
[
dcc.Store(id="noUpdate", data=True),
html.Button("Add", id="buttonAdd"),
html.Div(id="container1", children=[]),
html.Div(id="container2", children=[]),
]
)
@app.callback(
Output("container1", "children"),
Output("container2", "children"),
Output("noUpdate", "data"),
Input("buttonAdd", "n_clicks"),
prevent_initial_call=True,
)
def addButton(nClicks):
container1 = Patch()
container1.append(
dcc.Input(f"Input {nClicks}", id={"type": "input", "index": nClicks})
)
container2 = Patch()
container2.append(
html.Button(
f"Button {nClicks}", id={"type": "button", "index": nClicks}, n_clicks=3
)
)
return container1, container2, True
@app.callback(
Output({"type": "input", "index": MATCH}, "value"),
Input({"type": "button", "index": MATCH}, "n_clicks"),
Input("noUpdate", "data"),
prevent_initial_call=True,
)
def test(nClicks, _):
# Prevent this callback from running if it was triggered by the add button callback.
# Remove the following 2 lines to see the behavior without extra dcc.Store.
if "noUpdate" in callback_context.triggered_prop_ids.values():
raise PreventUpdate
print(callback_context.triggered)
return no_update
if __name__ == "__main__":
app.run(debug=True)
While this approach works great in the small example, it leads to many unnecessary callback evocation if the noUpdate store is an input to multiple callbacks. Callbacks now fire solely because they have noUpdate as Input and not because any of there real inputs have changed. Does anyone have a better suggestions how to ignore the callbacks created when adding a button? Is there some way to prevent subsequent callbacks or mark the source of a callback trigger?