In the example below I a have button that triggers 2 new items to be added to la…yout every time it is clicked -> a button and a div.
Clicking on the newly added button triggers an update on the div showing how many times the button was clicked, using pattern matching callbacks.
This is the callback used to add the new divs to the layout (via a dcc store that then updates via clientside callback, see full code below)
```
@app.callback(
Output("items-store", "data"),
Input("add-view", "n_clicks"),
prevent_initial_call=True,
)
def add_view(index):
return {
"left": left_view(index),
"right": right_view(index)
}
```
However, when this div exists inside a different parent, the click callback is triggered for EVERY button whenever I add a new button to layout.
This code does not cause the callbacks to be triggered
```
def right_view(index):
return html.Div([
])
def left_view(index):
return html.Div([
html.Button(f"Button {index}", id={"type": "action-btn", "index": index}, n_clicks=0, style={"height": "23px"}),
html.Div(f"Triggered 0", id={"type": "output-txt", "index": index}, style={"height": "23px"})
])
```
But this code DOES cause the callbacks to be triggered
```
def right_view(index):
return html.Div([
html.Div(f"Triggered 0", id={"type": "output-txt", "index": index}, style={"height": "23px"})
])
def left_view(index):
return html.Div([
html.Button(f"Button {index}", id={"type": "action-btn", "index": index}, n_clicks=0, style={"height": "23px"}),
])
```
The full discussion around this issue can be found [HERE](https://community.plotly.com/t/unexplained-callback-behavior/71128/10) with more animations of the bad behavior
```
from dash import html, Output, Input, State, ctx, MATCH, ALL, dcc, Dash
app = Dash( __name__, suppress_callback_exceptions=True)
def right_view(index):
return html.Div([
# This causes callbacks to fire whenever added to the layout
html.Div(f"Triggered 0", id={"type": "output-txt", "index": index}, style={"height": "23px"})
])
def left_view(index):
return html.Div([
html.Button(f"Button {index}", id={"type": "action-btn", "index": index}, n_clicks=0, style={"height": "23px"}),
# Moving the div here does not cause the callbacks to fire when added to the layout
# html.Div(f"Triggered 0", id={"type": "output-txt", "index": index}, style={"height": "23px"})
])
@app.callback(
Output({"type": "output-txt", "index": MATCH}, "children"),
Input({"type": "action-btn", "index": MATCH}, "n_clicks"),
Input({"type": "output-txt", "index": MATCH}, "children"),
prevent_initial_call=True
)
def inc_counter_for_view(clicks, triggered):
return f"Triggered {int(triggered.split()[1]) + 1}"
app.layout = html.Div([
html.Button("Add View", id="add-view"),
dcc.Store("items-store", data=[], storage_type="memory"),
html.Div([
html.Div([], id="left", style={"display": "flex", "flex-direction": "column", "gap": "5px"}),
html.Div([], id="right", style={"display": "flex", "flex-direction": "column", "gap": "5px"}),
],
style={"display": "flex", "flex-direction": "row", "gap": "5px", "margin-top": "5px"}
)]
)
app.clientside_callback(
"""
function(data, left, right) {
left.push(data.left)
right.push(data.right)
return [left, right]
}
""",
Output("left", "children"),
Output("right","children"),
Input("items-store", "data"),
State("left","children"),
State("right","children"),
prevent_initial_call=True
)
@app.callback(
Output("items-store", "data"),
Input("add-view", "n_clicks"),
prevent_initial_call=True,
)
def add_view(index):
return {
"left": left_view(index),
"right": right_view(index)
}
app.run_server(debug=True)
```