Multi Dropdown Alternative for Large Amount of Values

Hey guys,

So just wanted to write about an alternative to the multi dropdown. While I am a fan of the multi-dropdown component, after a certain amount of items, it just gets too big. I have tried limiting the height the component can get, but then it is hard to remove.

I used a single-column DataTable as an alternative, with checkboxes for each row, which can then be used as an input for another callback like this:

07%20PM

Similar to a CheckBox Multi Dropdown: https://ej2.syncfusion.com/documentation/multi-select/checkbox/

I added a select all and select none option, and made it so it’s a fixed size and scrollable.

Here is the code:

layout = html.Div([
    html.Div([
        html.Div(html.Button(
            'Select All', id='all-button-genre', className='all-button'),),
        html.Div(html.Button(
            'Select None', id='none-button-genre', className='none-button'),),
    ],
        className="multi-filter"
    ),
    html.Div([
        dash_table.DataTable(
            id='datatable-interactivity-genre',
            columns=[
                {"name": 'Genre', "id": 'value'}
            ],
            data=genr_l.to_dict('records'), #table that I defined at start
            n_fixed_rows=1,
            filtering=True,
            row_selectable="multi",
            virtualization=True,
            pagination_mode=False,
            style_table={
                'minHeight': '200px',
                'maxHeight': '200px',
            },
            css=[{
                'selector': '.dash-cell div.dash-cell-value',
                'rule': 'display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;'
            }],
            style_header={
                'backgroundColor': 'green',
                'color': 'white',
                'fontSize': '14px',
                'fontWeight': 'bold',
                'textAlign': 'center',
            },
            style_cell={
                'whiteSpace': 'no-wrap',
                'overflow': 'hidden',
                'textOverflow': 'ellipsis',
                'maxWidth': 0,
                'textAlign': 'left',
            },
            style_cell_conditional=[
                {
                    'if': {'row_index': 'odd'},
                    'backgroundColor': 'rgb(232, 232, 232)'
                }
            ],
            style_as_list_view=True,
        ),
        # html.Div(id='datatable-interactivity-container')
    ],
    ),
],
className='individual-filter'
),

This callback is to update the selected rows

@app.callback(
    [
    Output('datatable-interactivity-genre', "selected_rows"),
    ],
    [
    Input('all-button-genre', 'n_clicks'),
    Input('none-button-genre', 'n_clicks'),
    ],
    [
    State('datatable-interactivity-genre', "derived_virtual_data"),
    ]
)
def select_all_genre(all_clicks,none_clicks, selected_rows):
    if selected_rows is None:
        return [[]]
    ctx = dash.callback_context
    if not ctx.triggered:
        button_id = 'No clicks yet'
    else:
        button_id = ctx.triggered[0]['prop_id'].split('.')[0]


    
    if button_id == 'all-button-genre':
        return [[i for i in range(len(selected_rows))]]
    else:
        return [[]]

Once you have the rows, you can use the rows as a state to be the input in a filtering/cleaning process

@app.callback(
    Output('intermediate-value', 'children'),
    [
    Input('refresh-button', 'n_clicks'),
    Input('interval-component', 'n_intervals')
    ],
    [
    State('datatable-interactivity-genre', "derived_virtual_data"),
    State('datatable-interactivity-genre', "derived_virtual_selected_rows"),
    ]
)
def clean_data(n_clicks,n, genre_rows, genre_selected_rows):

    df = pd.read_pickle('table.pkl')

    ############
    # filtering
    if genre_selected_rows is not None and genre_selected_rows != []:
        df = df[df['genre'].isin([genre_rows[i]['value'] for i in genre_selected_rows])]
    

    ############
    

    datasets = {
        'df': df.to_json(orient='split')
    }

    return json.dumps(datasets)

Hope this helps someone! Cheers

2 Likes

Hi @louisrivers,

Thanks for sharing. I’ve moved your post to the Dash forum category so more folks working with Dash will see it.

-Jon