Remove child from dash component with partial property update

Hi all,
I’m developing a web app where the user can select different type of graphs by adding new dropdown elements and selecting the type of graph.
I already could implement the logic of adding the elements, displaying the corresponding graph and also remove a certain dropdown element by using the “remove” button.
Since the graphs in the later application are quite large I tried to do everything with the “Patch” class.

However, I’m struggling right now to implement the logic to also remove/delete the graph based on the removed dropdown element.
The code below should make things clearer:

from dash import Dash, html, Input, Output, dash, ALL, Patch, dcc, callback, State
import dash_bootstrap_components as dbc
import plotly.graph_objects as go


def graphs(name):
    fig = go.Figure(
        go.Scatter(
            x=[1,2,3],
            y=[11,4,5]
        )
    )

    return html.Div(children=[
        html.P(name),
        dcc.Graph(figure=fig)
    ])


@callback(
    Output("Visualizations", "children"),
    Input({"type": "Graphs", "index": ALL}, "value"),
    State("Visualizations", "children"),
)
def create_graphs(options, children):
    if len(options) != 0 and None not in options:
    
        patched_children_graphs = Patch()
        existing_graphs = []

        if len(children) != 0:
            for child in children:
                if child["props"]["children"][0]["type"] == "P":
                    existing_graphs.append(child["props"]["children"][0]["props"]["children"])
            

        if "Graph A" in options and "Graph A" not in existing_graphs:
            child = graphs("Graph A")
                
            patched_children_graphs.append(child)

        if "Graph B" in options and "Graph B" not in existing_graphs:
            child = graphs("Graph B")

            patched_children_graphs.append(child)

        if "Graph C" in options and "Graph C" not in existing_graphs:
            child = graphs("Graph C")

            patched_children_graphs.append(child)

        return patched_children_graphs
        
    else:
        return dash.no_update

@callback(
    Output("Options", "children", allow_duplicate=True),
    Input({"type": "Button-Remove", "index": ALL}, "n_clicks"),
    prevent_initial_call=True
    )
def remove_graph_type_option(clicks):
    if clicks:
        patched_children = Patch()

        try:
            values_to_remove = clicks.index(1)

            del patched_children[values_to_remove]

            return patched_children
        except:
            return dash.no_update


@callback(
    Output("Options", "children"),
    Input("Button-Add", "n_clicks"),
)
def add_graph_type_option(clicks):
    if clicks:
        patched_children = Patch()

        row = dbc.Row(children=[
                dbc.Col(children=[dcc.Dropdown(
                    placeholder="Graph Types", 
                    options=["Graph A", "Graph B", "Graph C"],
                    id={"type" : "Graphs", "index": clicks}
                )],
                width=1
                ),
                dbc.Col(children=[
                    dbc.Button(children=[
                        html.P("Remove")
                    ],
                    n_clicks=0,
                    outline=True, 
                    id={"type" : "Button-Remove", "index": clicks }
                )],
                width=1
                )
        ],
        id="Row-With-Options") 

        patched_children.append(row)

        return patched_children


layout = html.Div(
    children=[
        dbc.Row(children=[
            dbc.Row(children=[
                dbc.Col(children=[
                    html.Div(children=[

                    ],
                    id="Options"),
                ]),
            ]),
            dbc.Row(children=[
                dbc.Col(children=[
                    dbc.Button(children=[
                        html.P("Add"),
                        ],
                        n_clicks=0,
                        outline=True, 
                        id="Button-Add"
                        ),
                ],
                width=1),
            ])
        ],
        id="Options-Container",
        ),
        dbc.Row(children=[

        ],
        id="Visualizations")
    ]),

app = Dash()
app.layout = layout

if __name__ == "__main__":
    app.run(debug=True)

Can someone give me a hint how to add the missing logic?
Thanks!

Hello @Bakira,

The logic seems right, I’d put a print statement after the except and print the traceback. You could be keeping it from actually going out.

1 Like

Hi @jinnyzor, thanks for your quick reply.

I’m not sure if I understood correctly.

When I hit “Add” and i.e. select “Graph A” a graph with a header “Graph A” is displayed. If I click on “Remove” the dropwdown element for “Graph A” disappears but the graph stills exist. So my logic only removes the dropwdown element but not the graph.

Maybe my initial description was confusing, sorry.

It looks like you dont have anything to remove the children from Visualizations when removing.

Hi,

I changed the callback “remove_graph_type_option” as follows:

@callback(
    [
        Output("Options", "children", allow_duplicate=True),
        Output("Visualizations", "children", allow_duplicate=True)
    ],
    Input({"type": "Button-Remove", "index": ALL}, "n_clicks"),
    prevent_initial_call=True
    )
def remove_graph_type_option(clicks):
    if clicks:
        patched_children = Patch()
        patched_graphs = Patch()

        try:
            values_to_remove = clicks.index(1)

            del patched_children[values_to_remove]
            del patched_graphs[values_to_remove]

            return patched_children, patched_graphs
        except:
            return dash.no_update

Is there a better option? Should I remove the graphs in a seperate callback?

You’ll need to parse which index was the actual trigger. Use ctx for that.

Hello man, I’m facing a very similar issue

It’s working perfectly when I run it locally, but in the deployed version of AWS Elastic Beanstalk it does not work… I can’t see any error, but it simply don’t remove either add the children’s based on my clicks…

Have you faced any solution for this issue, mate? @Bakira