Circular callbacks for Dropdown Options

Hello Dash Community,

Currently, I am on a project involving 4 dropdowns acting like filters and 1 confirms button to update the Data Table. 4 dropdowns are mapping with 4 score columns in the Excel sheet, and every dropdown depends on the other 3 dropdowns.

The audience can choose or remove multiple values from the dropdown then other dropdowns will be reflected. After selecting values from dropdowns, the Data Table will be filtered based on the dropdown options once the audience clicking the “confirm” button. From my understanding, these dropdowns are mutually depending on each other. I am trying to mimic the Circular Callbacks from Dash documentation. Advanced Callbacks | Dash for Python Documentation | Plotly




dash.version : 1.19.0

As of now,
Issue 1: The dropdown options are not updating, in other words, they are not linking to each other.
Issue 2: I can only choose one value from the dropdown every time even though I set “multiple = True” in the layout.
Issue 3: If I click “x” to remove value from the dropdown, I am no longer have the ability to select again.

import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_table as dt
import pandas as pd


df = pd.read_csv("https://raw.githubusercontent.com/perryisbusy/Circular-Callbacks/main/Scores.csv")

app = dash.Dash()

app.layout = html.Div(children=[

    html.H2(children=["Testing Circular Callbacks"],
            style={"text-align": "center"}),

    html.Div(children=[
        dbc.Badge("Score 1", color="info", className="mr-1"),
        dcc.Dropdown(id="dropdown1",
                     clearable=True,
                     multi=True,
                     options=[]
                     )],
        style={'width': '24.5%', 'padding': '5px 3px', 'display': 'inline-block'}),

    html.Div(children=[
        dbc.Badge("Score 2", color="info", className="mr-1"),
        dcc.Dropdown(id="dropdown2",
                     clearable=True,
                     multi=True,
                     options=[]
                     )],
        style={'width': '24.5%', 'padding': '5px 3px', 'display': 'inline-block'}),

    html.Div(children=[
        dbc.Badge("Score 3", color="info", className="mr-1"),
        dcc.Dropdown(id="dropdown3",
                     clearable=True,
                     multi=True,
                     options=[]
                     )],
        style={'width': '24.5%', 'padding': '5px 3px', 'display': 'inline-block'}),

    html.Div(children=[
        dbc.Badge("Score 4", color="info", className="mr-1"),
        dcc.Dropdown(id="dropdown4",
                     clearable=True,
                     multi=True,
                     options=[]
                     )],
        style={'width': '24.5%', 'padding': '5px 3px', 'display': 'inline-block'}),

    html.Div(children=[
        html.Button('Confirm', id='confirm', n_clicks=0)]),

    html.Div(children=[dt.DataTable(id="data_table",
                                    columns=[{'id': c, 'name': c} for c in df.columns],
                                    data=df.to_dict('records'),
                                    style_cell={
                                        'overflow': 'hidden',
                                        'textOverflow': 'ellipsis',
                                        'minWidth': '100px', 'width': '150px', 'maxWidth': '180px',
                                    },
                                    filter_action="native",
                                    sort_action="native",
                                    style_table={'overflowX': 'scroll',
                                                 'overflowY': 'scroll',
                                                 "maxHeight": 400,
                                                 },

                                    )],
             style={'width': '100%', 'display': 'inline-block'})

])


@app.callback(
    [Output(component_id="dropdown1", component_property="options"),
     Output(component_id="dropdown2", component_property="options"),
     Output(component_id="dropdown3", component_property="options"),
     Output(component_id="dropdown4", component_property="options")],
    [Input(component_id="dropdown1", component_property="value"),
     Input(component_id="dropdown2", component_property="value"),
     Input(component_id="dropdown3", component_property="value"),
     Input(component_id="dropdown4", component_property="value")]
)
def update(dropdown1, dropdown2, dropdown3, dropdown4):
    ctx = dash.callback_context
    trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
    print(dropdown1, dropdown2, dropdown3, dropdown4)

    dff = df.copy()
    print(dff.shape)

    if trigger_id == "dropdown1":
        print(dff.shape)
        dff = dff[dff["Score 1"].isin(dropdown1)]
        print(dff.shape)
    elif trigger_id == "dropdown2":
        print(dff.shape)
        dff = dff[dff["Score 2"].isin(dropdown2)]
        print(dff.shape)
    elif trigger_id == "dropdown3":
        print(dff.shape)
        dff = dff[dff["Score 3"].isin(dropdown3)]
        print(dff.shape)
    elif trigger_id == "dropdown4":
        print(dff.shape)
        dff = dff[dff["Score 4"].isin(dropdown4)]
        print(dff.shape)
    else:
        print("None")

    score1_options = [{"label": item, "value": item} for item in dff['Score 1'].unique()]
    score2_options = [{"label": item, "value": item} for item in dff['Score 2'].unique()]
    score3_options = [{"label": item, "value": item} for item in dff['Score 3'].unique()]
    score4_options = [{"label": item, "value": item} for item in dff['Score 4'].unique()]

    return [score1_options, score2_options, score3_options, score4_options]


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

Up is my code, you can run the code directly and I also put code on my Github as well. Please kindly guide what I need to do. Thanks again and go Dash! :grinning: