✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

Dash: dynamically generated dropdowns not updating value (short, fully reproducible error)

I have a multi-option dropdown where a user selects a state. For each state they select, they get an option to choose counties for that state. The number of dropdowns on the page is equal to the number of states chosen.

The issue: the ‘value’ property of the dynamically generated dropdowns does not change. Here’s my code, with the ‘county-options’ div being printed at the bottom of the screen for debugging purposes. Notice that when you select California and then select either (or both) counties, the ‘value’ property of the dropdown is still None.


import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output
import pandas as pd
import numpy as np



app = dash.Dash(__name__)


states = ['California','New York']
county_options = {'California':['Los Angeles','Orange'],'New York':['Manhattan','Brooklyn']}
for state in county_options.keys():
    county_options[state] = [{'label':county,'value':county} for county in county_options[state]]

app.layout = html.Div(children=[
    html.Div('Select States(s)'),
    dcc.Dropdown(
        id='states',
        options=[{'label' : s,'value':s} for s in states],
        value=None,
        multi=True
    ),
    html.Div(id='county-options'),
    html.Div(id='for-debugging')])




@app.callback(
    Output('county-options','children'),
    [Input('states','value')])
def update_county_options(states):
    if not states:
        return None
    dropdowns = [
        [html.Div('Select Counties for {}:'.format(state)),
        dcc.Dropdown(
            id='county_dropdown_{}'.format(state),
            options=county_options[state],
            value=None,
            multi=True
        )] for state in states]
    return [item for dropdown in dropdowns for item in dropdown]    #flatten the above list of lists

@app.callback(
    Output('for-debugging','children'),
    [Input('county-options','children')])
def print_dynamic_dropdown(c):
    return str(c)

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

Hi @clumsy_coder welcome to the forum! The problem is not with the value of the multi-dropdowns but with the method chosen to display these values: the parent div (county-options) knows about which elements it contains but not about their values (so it displays the values which was used for initializing the children components). Below is a slightly modified code, using pattern-matching callbacks (introduced with dash 0.11) which prints directly the value of the dropdowns. If you execute the code you’ll see that the multi-dropdowns have the expected values.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output, ALL
import pandas as pd
import numpy as np



app = dash.Dash(__name__)


states = ['California','New York']
county_options = {'California':['Los Angeles','Orange'],'New York':['Manhattan','Brooklyn']}
for state in county_options.keys():
    county_options[state] = [{'label':county,'value':county} for county in county_options[state]]

app.layout = html.Div(children=[
    html.Div('Select States(s)'),
    dcc.Dropdown(
        id='states',
        options=[{'label' : s,'value':s} for s in states],
        value=None,
        multi=True
    ),
    html.Div(id='county-options'),
    html.Div(id='for-debugging')])


@app.callback(
    Output('county-options','children'),
    [Input('states','value')])
def update_county_options(states):
    if not states:
        return None
    dropdowns = [
        [html.Div('Select Counties for {}:'.format(state)),
        dcc.Dropdown(
            id={'type':'county_dropdown', 'index':state},
            options=county_options[state],
            value=None,
            multi=True
        )] for state in states]
    return [item for dropdown in dropdowns for item in dropdown]    #flatten the above list of lists


@app.callback(
    Output('for-debugging','children'),
    [Input({'type':'county_dropdown', 'index':ALL},'value')])
def print_dynamic_dropdown(values):
    output = ""
    for val in values:
        print(val)
        output += str(val)
    return output

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

Thanks you so much, @Emmanuelle ! This is exactly what I needed. I spent a lot of time trying to figure out why the dynamic dropdowns all had null values.

Regarding the callback that uses pattern matching, is there any way to know which dropdown a value came from? The “values” argument of print_dynamic_dropdown() is just a list of lists, but for example county X might belong to two different states and have the same name. I’d like to know which state chose county X.