Multi-Level Dropdowns with multi selection

How can I create a Multi-Level Dropdowns with multi selection in dash plotly?

My callback:

@app.callback(Output('selected_features_set', 'children'),[Input('statistics-specify', 'value'),Input('lags-specify', 'value'), Input('window-specify', 'value')])
def display_output(input_value_1, input_value_2, input_value_3):
    if len(input_value_1) == len(input_value_2) and  len(input_value_1) == len(input_value_3) and len(input_value_3) == len(input_value_2):
        for i in range(len(input_value_1)):
            return str(input_value_1[i] + '_' + input_value_2[i]+ '_' + input_value_3[i])
    else:
        return str("Selection error")

My dropdown menus:

dbc.Row(
            [
                dbc.Label('Select statistics:', className="my-label"),

                dbc.Col(
                    dcc.Dropdown(
                        id='statistics-specify',
                        className='dash-bootstrap',
                        options=[{'label': 'min', 'value': 'min'},
                                 {'label': 'max', 'value': 'max'},
                                 {'label': 'mean', 'value': 'mean'},
                                 {'label': 'standard deviation', 'value': 'std'},
                                 ],
                        value="mean",
                        multi=True,
                        clearable=False,style={'float': 'right',"width":"200px"}
                    ),
                ),
            ], className="mt-2",
        ),
        dbc.Row(
            [
                dbc.Label('Specify daily lags:', className="my-label"),

                dbc.Col(
                    dcc.Dropdown(
                        id='lags-specify',
                        className='dash-bootstrap',
                        options=[{'label': 'lag_0', 'value': 'lag_0'},
                                 {'label': 'lag_1', 'value': 'lag_1'},
                                 {'label': 'lag_2', 'value': 'lag_2'},
                                 {'label': 'lag_3', 'value': 'lag_3'},
                                 {'label': 'lag_4', 'value': 'lag_4'},
                                 {'label': 'lag_5', 'value': 'lag_5'},
                                 {'label': 'lag_6', 'value': 'lag_6'},
                                 {'label': 'lag_7', 'value': 'lag_7'},
                                 {'label': 'lag_8', 'value': 'lag_8'},
                                 {'label': 'lag_9', 'value': 'lag_9'},
                                 {'label': 'lag_10', 'value': 'lag_10'}
                                 ],
                        value="lag_0",
                        multi=True,
                        clearable=False,style={'float': 'right',"width":"200px"}
                    ),
                ),
            ], className="mt-2",
        ),
dbc.Row(
            [
                dbc.Label('Specify sliding window:', className="my-label"),

                dbc.Col(
                    dcc.Dropdown(
                        id='window-specify',
                        className='dash-bootstrap',
                        options=[{'label': 'window_0', 'value': 'window_0'},
                                 {'label': 'window_1', 'value': 'window_1'},
                                 {'label': 'window_2', 'value': 'window_2'},
                                 {'label': 'window_3', 'value': 'window_3'},
                                 {'label': 'window_4', 'value': 'window_4'},
                                 {'label': 'window_5', 'value': 'window_5'},
                                 {'label': 'window_6', 'value': 'window_6'},
                                 {'label': 'window_7', 'value': 'window_7'},
                                 {'label': 'window_8', 'value': 'window_8'},
                                 {'label': 'window_9', 'value': 'window_9'},
                                 {'label': 'window_10', 'value': 'window_10'},
                                 {'label': 'window_11', 'value': 'window_11'},
                                 {'label': 'window_12', 'value': 'window_12'},
                                 {'label': 'window_13', 'value': 'window_13'},
                                 {'label': 'window_14', 'value': 'window_14'},
                                 {'label': 'window_15', 'value': 'window_15'},
                                 {'label': 'window_16', 'value': 'window_16'},
                                 {'label': 'window_17', 'value': 'window_17'},
                                 {'label': 'window_18', 'value': 'window_18'},
                                 {'label': 'window_19', 'value': 'window_19'},
                                 {'label': 'window_20', 'value': 'window_20'},
                                 ],
                        value="window_20",
                        multi=True,
                        clearable=False,style={'float': 'right',"width":"200px"}
                    ),
                ),

My result:
Screenshot 2022-03-31 134211

But I want to be able to display all the selections.
My desired result would be:

max_lag_3_window_1
mean_lag_1_window_2

Could you help me?

Using two callbacks.

Hello Stu! I added my code and my result. What do you mean by two callbacks?

Hi @starry ! I think if you change your callback to this it should work:

@app.callback(
    Output('selected_features_set', 'children'),
    [Input('statistics-specify', 'value'),Input('lags-specify', 'value'), Input('window-specify', 'value')])
def display_output(input_value_1, input_value_2, input_value_3):
    if len(input_value_1) == len(input_value_2) and  len(input_value_1) == len(input_value_3) and len(input_value_3) == len(input_value_2):
        output = [input_value_1[i] + '_' + input_value_2[i]+ '_' + input_value_3[i] for i in range(len(input_value_1))]
        return '\n'.join(output)
    else:
        return str("Selection error")

As you have it right now the callback finishes in the first loop of the loop, that’s why it only prints the first element. I changed it to a list comprehension, which later gets returned a string where all the created names are pasted with a line break (\n) between them.

In addition, to make your code shorter (and faster to write!) you can create the dropdown options like this:

[{'label': f'window_{i}', 'value': f'window_{i}'} for i in range(21)]

One triggers the other one. Like this,

@app.callback(Output('dropdown2', 'options'), Input('dropdown1', 'value'))
def dropdown1(value):
    if value == some conditions:
        return filtered(options)

@app.callback(Output('your-output-components','children'),
              [Input('dropdown1', 'value'), Input('dropdown2', 'value'))
def dropdown2(value1,value2):
    if value2 is None:
        raise preventUpdate
    return something(value1, value2)

Hello @celia The first suggestion did not work :frowning: but thanks very much for the second hint, very helpful :slight_smile:

Hello @stu ! I am afraid there is some misunderstanding. I do not want to filter anything. The values of the 3 dropdown menus are fixed no matter of the choice of any of them. They do not depend on one another. I want to be able to select one choice for each of the dropdown menus and display this selection. Moreover, I want the user to be able to make multiple selection, and see such selections

Oh, that’s what we usually call a multi-level drop-down.
Is itertools.product() what you want?

if len(set(len(i) for i in [input_value_1, input_value_2, input_value_3])) ==1: 
    return ['_'.join(i) for i in itertools.product(input_value_1, input_value_2, input_value_3)]

@stu tried this solution, but once I add 2, it destroys my dashboard
Screenshot 2022-03-31 162030