Hi @jinnyzor
Thanks for posting that app from the Dash Example Index!
I just did a pull request to simplify that example - it will now have two callbacks - one to add a card, another to delete. It deletes simply by hiding it: style={"display": "none"}
This is easier, but if you have hundreds of components added dynamically, then the original way is better because it actually removes it from the dom.
Here are both ways:
1. Delete (hide) an element:
from dash import Dash, dcc, html, Input, Output, State, MATCH
import plotly.express as px
import dash_bootstrap_components as dbc
df = px.data.gapminder()
default_column_x = "year"
default_column_y = "gdpPercap"
options = ["lifeExp", "year", "pop", "gdpPercap"]
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container(
dbc.Row(
dbc.Col(
[
html.H3("Pattern Matching Callbacks Demo"),
dbc.InputGroup(
[
dcc.Dropdown(
options=df.country.unique(),
value="Canada",
id="pattern-match-country",
clearable=False,
style={"width": 300},
),
dbc.Button(
"Add Chart", id="pattern-match-add-chart", n_clicks=0
),
],
className="mb-3",
),
html.Div(id="pattern-match-container", children=[], className="mt-4"),
]
)
),
fluid=True,
)
def create_figure(column_x, column_y, country):
chart_type = px.line if column_x == "year" else px.scatter
return (
chart_type(
df.query("country == '{}'".format(country)),
x=column_x,
y=column_y,
)
.update_layout(
title="{} {} vs {}".format(country, column_x, column_y),
margin_l=10,
margin_r=0,
margin_b=30,
)
.update_xaxes(title_text="")
.update_yaxes(title_text="")
)
def make_card(n_clicks, country):
return dbc.Card(
[
dbc.CardHeader(
[
f"Figure {n_clicks + 1} ",
dbc.Button(
"X",
id={"type": "dynamic-delete", "index": n_clicks},
n_clicks=0,
color="secondary",
),
],
className="text-end",
),
dcc.Graph(
id={"type": "dynamic-output", "index": n_clicks},
style={"height": 300},
figure=create_figure(default_column_x, default_column_y, country),
),
dcc.Dropdown(
id={"type": "dynamic-dropdown-x", "index": n_clicks},
options=options,
value=default_column_x,
clearable=False,
),
dcc.Dropdown(
id={"type": "dynamic-dropdown-y", "index": n_clicks},
options=options,
value=default_column_y,
clearable=False,
),
],
style={
"width": 400,
"display": "inline-block",
},
className="m-1",
id={"type": "dynamic-card", "index": n_clicks},
)
@app.callback(
Output("pattern-match-container", "children"),
Input("pattern-match-add-chart", "n_clicks"),
State("pattern-match-container", "children"),
State("pattern-match-country", "value"),
)
def add_card(n_clicks, cards, country):
new_card = make_card(n_clicks, country)
cards.append(new_card)
return cards
@app.callback(
Output({"type": "dynamic-card", "index": MATCH}, "style"),
Input({"type": "dynamic-delete", "index": MATCH}, "n_clicks"),
prevent_initial_call=True,
)
def remove_card(_):
return {"display": "none"}
@app.callback(
Output({"type": "dynamic-output", "index": MATCH}, "figure"),
Input({"type": "dynamic-dropdown-x", "index": MATCH}, "value"),
Input({"type": "dynamic-dropdown-y", "index": MATCH}, "value"),
Input("pattern-match-country", "value"),
)
def update_figure(column_x, column_y, country):
return create_figure(column_x, column_y, country)
if __name__ == "__main__":
app.run_server(debug=True)
2. Delete (remove) and element
from dash import Dash, dcc, html, Input, Output, State, ALL, MATCH, ctx
import plotly.express as px
import dash_bootstrap_components as dbc
df = px.data.gapminder()
default_column_x = "year"
default_column_y = "gdpPercap"
options = ["lifeExp", "year", "pop", "gdpPercap"]
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container(
dbc.Row(
dbc.Col(
[
html.H3("Pattern Matching Callbacks Demo"),
dbc.InputGroup(
[
dcc.Dropdown(
options=df.country.unique(),
value="Canada",
id="country",
clearable=False,
style={"width": 300},
),
dbc.Button("Add Chart", id="add-chart", n_clicks=0),
],
className="mb-3",
),
html.Div(id="container", children=[], className="mt-4"),
]
)
),
fluid=True,
)
def create_figure(column_x, column_y, country):
chart_type = px.line if column_x == "year" else px.scatter
return (
chart_type(
df.query("country == '{}'".format(country)),
x=column_x,
y=column_y,
)
.update_layout(
title="{} {} vs {}".format(country, column_x, column_y),
margin_l=10,
margin_r=0,
margin_b=30,
)
.update_xaxes(title_text="")
.update_yaxes(title_text="")
)
def make_card(n_clicks, country):
return dbc.Card(
[
dbc.CardHeader(
[
f"Figure {n_clicks + 1} ",
dbc.Button(
"X",
id={"type": "dynamic-delete", "index": n_clicks},
n_clicks=0,
color="secondary",
),
],
className="text-end",
),
dcc.Graph(
id={"type": "dynamic-output", "index": n_clicks},
style={"height": 300},
figure=create_figure(default_column_x, default_column_y, country),
),
dcc.Dropdown(
id={"type": "dynamic-dropdown-x", "index": n_clicks},
options=options,
value=default_column_x,
clearable=False,
),
dcc.Dropdown(
id={"type": "dynamic-dropdown-y", "index": n_clicks},
options=options,
value=default_column_y,
clearable=False,
),
],
style={
"width": 400,
"display": "inline-block",
},
className="m-1",
)
@app.callback(
Output("container", "children"),
Input("add-chart", "n_clicks"),
Input({"type": "dynamic-delete", "index": ALL}, "n_clicks"),
State("container", "children"),
State("country", "value"),
)
def add_delete_card(n_clicks, _, cards, country):
if ctx.triggered_id == "add-chart" or not ctx.triggered_id:
new_card = make_card(n_clicks, country)
cards.append(new_card)
else:
# exclude the deleted chart
delete_chart_number = ctx.triggered_id["index"]
cards = [
card
for card in cards
if "'index': " + str(delete_chart_number) not in str(card)
]
return cards
@app.callback(
Output({"type": "dynamic-output", "index": MATCH}, "figure"),
Input({"type": "dynamic-dropdown-x", "index": MATCH}, "value"),
Input({"type": "dynamic-dropdown-y", "index": MATCH}, "value"),
Input("country", "value"),
)
def display_output(column_x, column_y, country):
return create_figure(column_x, column_y, country)
if __name__ == "__main__":
app.run_server(debug=True)