Adding a 'Select All' Button to a Multi-Select Dropdown

I wanted to add a button, which selects all the options in my drop-down menu, except I don’t know how to return such a command.

Any help?

A workaround would be adding an ‘All’ option in the dropdown, and somehow making sure that when it is selected, all the other options clear, but it sound like more of a hassle, right?

1 Like

What you need to do here is have the values of your drop down the Output and the options as a State of some other callback, the Input can be some other component such as a checklist. Here is an example:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State

app = dash.Dash()

app.layout = html.Div([

    dcc.Dropdown(id='dropdown', multi=True,
                 options=[{'label': i, 'value': i} for i in range(10)], value=[1]),
    dcc.Checklist(id='select-all',
                  options=[{'label': 'Select All', 'value': 1}], values=[])
])


@app.callback(
    Output('dropdown', 'value'),
    [Input('select-all', 'values')],
    [State('dropdown', 'options'),
     State('dropdown', 'value')])
def test(selected, options, values):
    print(selected)
    if selected[0] == 1:
        return [i['value'] for i in options]
    else:
        return values


if __name__ == '__main__':
    app.run_server(debug=True)

3 Likes

@mikesmith1611

This really works in order to select all.
But is it possible to undo the tick mark of the select-all Checklist once you start changing some of the items in the Dropdown?

Say I did press the Checklist ‘select-all’, which populates the dropdown with multiple values (let say total 10 values). Now If I make changes to the dropdown, i.e remove one or two values from the multi-select dropdown list. How would I update the state of ‘select-all’ checklist (remove the tick mark)?

When I tried to do what I did below. It shows ‘Error loading dependencies’

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State

app = dash.Dash()

app.layout = html.Div([

    dcc.Dropdown(id='dropdown', multi=True,
                 options=[{'label': i, 'value': i} for i in range(10)], value=[1]),
    dcc.Checklist(id='select-all',
                  options=[{'label': 'Select All', 'value': 1}], values=[])
])

@app.callback(
    Output('dropdown', 'value'),
    [Input('select-all', 'values')],
    [State('dropdown', 'options'),
     State('select-all', 'options')])
def test(selected, options_1, options_2):
    if len(selected) > 0:
        return [i['value'] for i in options_1]
    else:
        return []


@app.callback(
    Output('select-all', 'values'),
    [Input('dropdown', 'value')],
    [State('dropdown', 'options')])
def tester(selected, options_1):
    print(selected)
    if len(selected) < len(options_1):
        return []


if __name__ == '__main__':
    app.run_server(debug=True, port=8051)

Hi,

Your seeing this error because of a circular dependency between Inputs and Outputs which is not allowed in Dash. You can see this if you hit F12 and go to console (in chrome).

This is a limitation of Dash which I have been stuck on several times.

This is an eleborate workaround! Instead of using the ‘select-all’ values as an output is uses a parent container and alters it’s children. I had to include some PreventUpdate calls to stop an infinite loop!

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State
from dash.exceptions import PreventUpdate
app = dash.Dash()

app.layout = html.Div([

    dcc.Dropdown(id='dropdown', multi=True,
                 options=[{'label': i, 'value': i} for i in range(10)], value=[1]),
    html.Div([
        dcc.Checklist(id='select-all',
                    options=[{'label': 'Select All', 'value': 1}], values=[])
    ], id='checklist-container')
])

@app.callback(
    Output('dropdown', 'value'),
    [Input('select-all', 'values')],
    [State('dropdown', 'options')])
def test(selected, options):
    if len(selected) > 0:
        return [i['value'] for i in options]
    raise PreventUpdate()


@app.callback(
    Output('checklist-container', 'children'),
    [Input('dropdown', 'value')],
    [State('dropdown', 'options'),
     State('select-all', 'values')])
def tester(selected, options_1, checked):

    if len(selected) < len(options_1) and len(checked) == 0:
        raise PreventUpdate()

    elif len(selected) < len(options_1) and len(checked) == 1:
        return  dcc.Checklist(id='select-all',
                    options=[{'label': 'Select All', 'value': 1}], values=[])

    elif len(selected) == len(options_1) and len(checked) == 1:
        raise PreventUpdate()

    return  dcc.Checklist(id='select-all',
                    options=[{'label': 'Select All', 'value': 1}], values=[1])

if __name__ == '__main__':
    app.run_server(debug=True, port=8051)

I’m not sure that this behaviour will ever change in Dash.

2 Likes

This is a great workaround. I was unaware of the PreventUpdate() function. I think this will help me to hack together a pretty good select all, unselect all checklist functionality as of now.

Hello,

I would like to use this workaround with a picklist suggesting some initial default values.

First issue is that the 1st time the page is loaded, the list shows the X items I want to suggest, but the Bar graph doesnt display anything until i pick a X+1th item in the list. I tried to change a bit the logic in the code but without success.

Additionally, I aim to add either a button or another Checklist to reset the list to the default suggestions, but prior to this I have to fix the first issue.

It makes sense because the graph presents the sales of ~50 products, but 10 out of 50 represents 95% of the sales. So, by default, I want to show the sales figures for these 10 products.

Below a code illustrating the current the problem, with the workaround presented above.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_daq as daq
from dash.dependencies import Input, Output, State

import plotly.graph_objs as go
from dash.exceptions import PreventUpdate

xTime=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

listofY=['y1','y2','y3','y4','y5','y6','y7','y8','y9','y10']

Table={
       'y1':[20, 14, 25, 16, 18, 22, 19, 15, 12, 16, 14, 17],
       'y2':[19, 18, 22, 14, 15, 19, 15, 14, 14, 21, 16, 16],
       'y3':[11, 14, 21, 16, 16, 10, 15, 11, 10, 12, 11, 16],
       'y4':[14, 10, 27, 14, 16, 14, 15, 18, 14, 12, 12, 16],
       'y5':[16, 15, 22, 18, 18, 19, 15, 13, 15, 12, 18, 17],
       'y6':[18, 17, 25, 14, 16, 11, 13, 14, 20, 14, 12, 18],
       'y7':[11, 14, 22, 14, 13, 19, 15, 17, 14, 12, 17, 19],
       'y8':[13, 14, 20, 14, 16, 17, 15, 14, 10, 13, 14, 20],
       'y9':[15, 15, 22, 15, 11, 13, 15, 14, 11, 14, 12, 22],
       'y10':[10, 11, 22, 14, 16, 21, 15, 14, 10, 12, 11, 16]
       }

app = dash.Dash()

app.layout = html.Div([
    html.Div([
        dcc.Dropdown(
            id='product-choice',
            options=[{'label': i, 'value': i} for i in listofY], 
            value=['y1', 'y2', 'y5'],
            multi=True
        ),
        html.Div([
            dcc.Checklist(
                id='select-all',
                options=[{'label': 'Select All', 'value': 1}], 
                values=[]
            )
        ], id='checklist-container')
    ]),
    
    html.Div(children=[
        dcc.Graph(
            id='bar-graph-by-model'
        ),
    ]),                
])

# Below the Workaround suggested by mikesmith1611
#@app.callback(
#    Output('product-choice', 'value'),
#    [Input('select-all', 'values')],
#    [State('product-choice', 'options')])
#def test(selectALL, options):
#    if len(selectALL) > 0:
#        return [i['value'] for i in options]
#    else:
#        raise PreventUpdate()
#
#@app.callback(
#    Output('checklist-container', 'children'),
#    [Input('product-choice', 'value')],
#    [State('product-choice', 'options'),
#     State('select-all', 'values')])
#def tester(chosenValues, availableChoices, selectALL):
#    
#    if len(chosenValues) < len(availableChoices) and len(selectALL) == 0:
#        raise PreventUpdate()
#
#    elif len(chosenValues) < len(availableChoices) and len(selectALL) == 1:
#        return  dcc.Checklist(id='select-all',
#                    options=[{'label': 'Select All', 'value': 1}], values=[])
#
#    elif len(chosenValues) == len(availableChoices) and len(selectALL) == 1:
#        raise PreventUpdate()
#
#    return  dcc.Checklist(id='select-all',
#                    options=[{'label': 'Select All', 'value': 1}], values=[1])  
#    
        
@app.callback(
        Output('bar-graph-by-model', 'figure'),
        [Input('product-choice', 'value')]
)
def updategraph(value):
    traces=[]
    for i in value:
        x=xTime
        ytemp=Table[i]
        traces.append(go.Bar(
            x=x,
            y=ytemp,
            name=i,
        ))
    return {
        'data': traces,
        'layout': go.Layout(
            )
   }
        
if __name__ == '__main__':
   app.run_server(debug=True, port=8051)

If the list shows the suggested values when the page is loaded, why does the graph stays empty ?

Hi @mikesmith1611 and @David22

I needed a similar thing and thank you for providing the idea of doing it. But, I have another question, what if I have a large set of options? doing a select all will crowd my display. How to handle such situations?

On my side I gave up with this workaround; At the end I had too many circular dependencies, and one is enough to break down everything :confused:

Hi @David22.

i’m not sure if your issue was resolved and/or what you mean by the graph staying empty when loaded. I tried your code as-is and this is what i get:

hi @mini_geek,

You basically need to uncomment the commented lines; I didnt notice that I copy-pasted my code with these lines commented.

Once done, if you reload the page, you can notice that the graph stays empty. It doesnt load the initial value in the picklist. Unless you actually pick a 4th item from the picklist.

So the idea is to have the same graph as I showed with a select all checkbox? @David22

@mini_geek Yes, and this checkbox should

  1. select all items from the picklist when checked,
  2. update itself as “unticked” once we remove items from the picklist.

Current code works fine for 1) and 2), but when the page loads the first time, and the picklist is filled with the default values, then the graph is not updated accordingly

I’ve managed to add a “Select All” option, and also show all data available when the dropdown is left blank.
Check this out: https://corporate-dash.herokuapp.com/ (explained in an article here: https://gabri-albini.medium.com/create-a-professional-dasbhoard-with-dash-and-css-bootstrap-e1829e238fc5)

It has been quite a while but I thought it is still nice to throw in another potential solution. Instead of using checkbox, I used a button. The button comes with an attribute named n_clicks. Therefore, I write code that updates the dropdown based on if the nclicks is even or odd.

import dash
from flask import Flask
from dash import html
from dash import dcc
from dash.dependencies import Input, Output, State, ClientsideFunction
from dash.exceptions import PreventUpdate
app = dash.Dash()

app.layout = html.Div([

dcc.Dropdown(id='dropdown', multi=True,
             options=[{'label': i, 'value': i} for i in range(10)], value=[1]),

html.Button('Select/Unselect all', id='select_all', n_clicks=0),

])

@app.callback(
Output(‘dropdown’, ‘value’),
[Input(‘select_all’, ‘n_clicks’)],
[State(‘dropdown’, ‘options’)])

def update_dropdown(n_clicks, feature_options):

ctx = dash.callback_context
if not ctx.triggered:
    raise PreventUpdate()
else:
    trigged_id = ctx.triggered[0]['prop_id'].split('.')[0]

    if trigged_id == 'select_all':
        if n_clicks == 0: ## Do not update in the beginning
            raise PreventUpdate()

        if n_clicks % 2 == 0: ## Clear all options on even clicks
            return []
        else: ## Select all options on odd clicks
            return [i['value'] for i in feature_options]
    else:
        raise PreventUpdate()

if name == ‘main’:
app.run_server(debug=True, port=8051)

Hope this can provide an alternative! Cheers!

Select/Unselect button in Plotly dash DropDown with CSS

Callback

@app.callback([Output('dropdown', 'value'),
               Output('button', 'children'),
               Output('button', 'className')],
              Input('button', 'n_clicks'))
def update_all(n_clicks):
    if (n_clicks % 2) == 0:
        return [], 'Select (All)', 'buttonGreen'
    else:
        return [1, 2, 3, 4, 5, 6], 'Unselect (All)', 'buttonBlink'

CSS Style (in Assets)

/* Bouton vert (ex: pour dire que les données sont chargées */
.buttonGreen {
    color: #fff;
    background-color: #68a360;
    border-color: #343d33;
    display: inline-block;
    margin-top: 4px;
    font-weight: 400;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    border: 1px solid transparent;
    padding: .375rem .75rem;
    font-size: 1rem;
    line-height: 1.5;
    border-radius: .25rem;
    transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}

/* Bouton clignotant rouge */
.buttonBlink {
    color: #fff;
    border-color: #33363d;
    display: inline-block;
    margin-top: 4px;
    font-weight: 400;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    border: 1px solid transparent;
    padding: .375rem .75rem;
    font-size: 1rem;
    line-height: 1.5;
    border-radius: .25rem;
    transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
  @keyframes glowing {
    0% {
      background-color: #d47c7c;
    }
    50% {
      background-color: #da4e4e;
      box-shadow: 0 0 10px #da4e4e;
    }
    100% {
      background-color: #d47c7c;
    }
  }
  .buttonBlink {
    animation: glowing 2000ms infinite;
  }

I wrote the following two callbacks in order to add both an “Select All” and “Deselect All” checkboxes to an dropdown. Also some extra code to auto uncheck to opposite box.

@callback(
    Output("aircraft-type-selector", "children"),
    [Input("aircraft-type-selector", "children")]
)
def create_ac_type_selector(children):
    cnx, cursor = database_connection(cfg)
    query = "SELECT DISTINCT [Aircraft type] FROM vliegtuiggeluid"
    aircraft_list  = [aircraft[0] for aircraft in cursor.execute(query).fetchall() if aircraft[0] is not None]
    options = [
        {
            "label": html.Span(
                [
                    html.I(className="fa-solid fa-plane"),
                    html.Span(aircraft, className='dropdown-ac-icon', style={'padding-left': 10})
                ], className="dropdown-label-style"
            ),
            "value": aircraft
        }
        for aircraft in aircraft_list
    ]
    return html.Div(
        children=[
            html.Div(
                [
                    dbc.Checklist(
                        id="ac-type-select-all-checkbox",
                        options=[{'label': 'Select All', 'value': 'select_all'}],
                        value=[],
                        className="checklist SelectAll"
                    ),
                    dbc.Checklist(
                        id='ac-type-deselect-all-checkbox',
                        options=[{'label': 'Deselect All', 'value': 'deselect_all'}],
                        value=[],
                        className="checklist DeselectAll"
                    ),
                ],
                className="checklist-container"
            ),
            dcc.Dropdown(
                id='aircraft-selector',
                options=options,
                value=[],
                searchable=True,
                multi=True,
                placeholder="Select an aircraft type",
            )
        ]
    )

@callback(
    Output('aircraft-selector', 'value'),
    Output('ac-type-select-all-checkbox', 'value'),
    Output('ac-type-deselect-all-checkbox', 'value'),
    Input('ac-type-select-all-checkbox', 'value'),
    Input('ac-type-deselect-all-checkbox', 'value'),
    State('aircraft-selector', 'value'),
    State('aircraft-selector', 'options'),
    prevent_initial_call=True
)
def manage_selections(select_all, deselect_all, selected_values, options):
    ctx = callback_context
    triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]

    all_values = [option['value'] for option in options]

    if triggered_id == 'ac-type-select-all-checkbox' and select_all:
        return all_values, select_all, []
    elif triggered_id == 'ac-type-deselect-all-checkbox' and deselect_all:
        return [], [], deselect_all
    elif triggered_id == 'aircraft-selector':
        if set(selected_values) == set(all_values):
            return selected_values, ['select_all'], []
        elif not selected_values:
            return selected_values, [], ['deselect_all']
        return selected_values, [], []

    return selected_values, [], []