Dependent Dropdown in Pattern-Matching Example

I am learning to use pattern-matching in my own dashboard by working with this example. In the example, how would I add an extra dropdown, such that the values of the dropdown with id=“pattern-match-country” depend on what is returned by the new dropdown?

I know that a new callback is required to provide the dropdown contents based on the values of the first dropdown, such as in this example:

However, I can’t seem to modify the callback with add_card in the pattern-matching example so that it takes a new Input from the new callback appropriately. My guess is to use MATCH to properly receive the values to display in the dropdown.

Hopefully all this makes sense. Thanks!

Hey @ragarre, I’m not sure if I understand your question. I think, you want to change the options of the dropdowns in the dynamically created components, right?

here an example of how to do this. It’s basically the example form the dash-example-index with a few lines of added code (which I marked clearly :wink: )

from dash import Dash, dcc, html, Input, Output, State, MATCH
import plotly.express as px
import dash_bootstrap_components as dbc
from dash.exceptions import PreventUpdate

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",
                    ),
                    # ---------------------------------------------------------------------------ADDED
                    dcc.Dropdown(
                        id={"type": "dynamic-dropdown-added", "index": n_clicks},
                        options=[1, 2, 3, 4]
                    )
                    # ---------------------------------------------------------------------------ADDED
                ],
                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)

# ---------------------------------------------------------------------------ADDED
@app.callback(
    Output({"type": "dynamic-dropdown-x", "index": MATCH}, "options"),
    Output({"type": "dynamic-dropdown-y", "index": MATCH}, "options"),
    Input({"type": "dynamic-dropdown-added", "index": MATCH}, "value"),
    prevent_initial_call=True
)
def limit_options(num):
    if not num:
        return options, options

    return [options[:num], options[:num]]
# ---------------------------------------------------------------------------ADDED


if __name__ == "__main__":
    app.run_server(debug=True)
1 Like

This was very helpful; thank you!

2 Likes