Pattern call-backs regarding adding dynamic graphs

Hi!

I got this to work by adding a button with an “X” that would trigger the callback to remove the chart.

Here is this feature added to the example app from 📣 Dash v1.11.0 Release - Introducing Pattern-Matching Callbacks

import dash
from dash.dependencies import Input, Output, State, ALL, MATCH
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
import json

df = px.data.gapminder()

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        html.Div(
            children=[
                dcc.Dropdown(
                    options=[{"label": i, "value": i} for i in df.country.unique()],
                    value="Canada",
                    id="country",
                    style={"display": "inline-block", "width": 200},
                ),
                html.Button(
                    "Add Chart",
                    id="add-chart",
                    n_clicks=0,
                    style={"display": "inline-block"},
                ),
            ]
        ),
        html.Div(id="container", children=[]),
    ]
)


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="")
    )


@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 display_dropdowns(n_clicks, _, children, country):
    default_column_x = "year"
    default_column_y = "gdpPercap"

    input_id = dash.callback_context.triggered[0]["prop_id"].split(".")[0]
    if "index" in input_id:
        delete_chart = json.loads(input_id)["index"]
        children = [
            chart
            for chart in children
            if "'index': " + str(delete_chart) not in str(chart)
        ]
    else:
        new_element = html.Div(
            style={
                "width": "23%",
                "display": "inline-block",
                "outline": "thin lightgrey solid",
                "padding": 10,
            },
            children=[
                html.Button(
                    "X",
                    id={"type": "dynamic-delete", "index": n_clicks},
                    n_clicks=0,
                    style={"display": "block"},
                ),
                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=[{"label": i, "value": i} for i in df.columns],
                    value=default_column_x,
                ),
                dcc.Dropdown(
                    id={"type": "dynamic-dropdown-y", "index": n_clicks},
                    options=[{"label": i, "value": i} for i in df.columns],
                    value=default_column_y,
                ),
            ],
        )
        children.append(new_element)
    return children


@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)

3 Likes