Delete a "div" created by dynamic callback


@app.callback(Output('container', 'children'),
              [Input('add_chart', 'n_clicks')],
              [State('container', 'children')])
def add_chart(n_clicks, div_container):
    new_children = html.Div(
        style={'width': '50%', 'height': '50%', 'display': 'inline-block', 'outline': 'thin lightgrey solid', 'padding': 10},
        children=[
            html.Button("Delete", id={'type': 'delete_graph', 'index': n_clicks}),
            dcc.Dropdown(options=[{'label': 'Line Chart', 'value': 'line'},
                                  {'label': 'Histogram Chart', 'value': 'histogram'},
                                  {'label': 'Scatter Chart', 'value': 'scatter'}, ], value='line',
                         id={'type': 'chart_type', 'index': n_clicks}),

            dcc.Dropdown(options=[{'label': col, 'value': col} for col in df.columns],
                         id={'type': 'dynamic_xaxis', 'index': n_clicks}),


            dcc.Dropdown(options=[{'label': col, 'value': col} for col in df.columns],
                         id={'type': 'dynamic_yaxis', 'index': n_clicks}),

            dcc.Graph(id={'type': 'dynamic_graph', 'index': n_clicks})
    ])
    div_container.append(new_children)
    return div_container


@app.callback(Output({'type': 'dynamic_graph', 'index': MATCH}, 'figure'),
              [Input({'type': 'dynamic_xaxis', 'index': MATCH}, 'value'),
               Input({'type': 'dynamic_yaxis', 'index': MATCH}, 'value'),
               Input({'type': 'chart_type', 'index': MATCH}, 'value'),
               Input({'type': 'delete_graph', 'index': MATCH}, 'n_clicks'),
               Input('container', 'children'), ],
              prevent_initial_call=True)
def update_graph(dynamix_xaxis, dynamix_yaxis, chart_type, delete, container):

    triggered = [t["prop_id"] for t in dash.callback_context.triggered]
    delete = len([1 for i in triggered if i == "delete_graph.n_clicks"])
    if delete:
        del container[-1]
    if chart_type == "line":
        fig = px.line(df, x=df[dynamix_xaxis], y=df[dynamix_yaxis])
    elif chart_type == "scatter":
        fig = px.scatter(df, x=df[dynamix_xaxis], y=df[dynamix_yaxis])
    else:
        fig = px.histogram(df, x=df[dynamix_xaxis], y=df[dynamix_yaxis])
    return fig



if __name__ == "__main__":
    app.run_server(debug=False)

After a div for graph is added, I added a button for delete option but it not working

@SaadKhan,

It looks like you might benefit from this:

You can use it to dynamically add and delete elements based upon index structure.

Here is an example put together by @AnnMarieW:

Your delete function will need to tie into the update of the container children. This is where you’ll have to pattern-match as well.

2 Likes

@jinnyzor Thank you so much, its done

2 Likes

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)
2 Likes

@AnnMarieW Thank you