I got an app that contains a button with callback function to create an unlimited amount of dropdowns which will be automatically id'ed as 'dropdown-i'. The struggle is that I don't seem to be able to actually use the values I input in these Dropdowns in another callback function (that's only trying to print them).
How can I retrieve these values or how would you do this?
Apparently the part value=dcc.Dropdown(id=dropdown_id).value
doesn't work.
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, State
app = dash.Dash(__name__)
app.layout = html.Div([
html.Button("Add Dropdown and Number Field", id="add-button"),
html.Div(id="input-container", children=[]),
html.Div(id="output"),
])
@app.callback(
Output("input-container", "children"),
Input("add-button", "n_clicks"),
State("input-container", "children")
)
def add_input(n_clicks, existing_children):
if n_clicks is None:
return existing_children
new_input = dbc.Row([
dbc.Col(dcc.Dropdown(
options=[
{'label': 'Option 1', 'value': 'option-1'},
{'label': 'Option 2', 'value': 'option-2'},
# Add more dropdown options as needed
],
value='option-1',
id=f'dropdown-{n_clicks}'
)),
dbc.Col(dcc.Input(
type='number',
value=0,
id=f'weight-{n_clicks}'
)),
])
existing_children.append(new_input)
return existing_children
@app.callback(
Output("output", "children"),
Input("add-button", "n_clicks"),
State("input-container", "children")
)
def process_dropdowns(n_clicks, dropdown_children):
if n_clicks is None:
return []
# Create a list to store the selected values from each dropdown
selected_values = []
# Iterate through the dropdowns to retrieve their values
for i, child in enumerate(dropdown_children):
dropdown_id = f'dropdown-{i+1}'
selected_value = dcc.Dropdown(id=dropdown_id).value
selected_values.append(selected_value)
# Process the selected values or use them as needed
return f"Selected Dropdown Values: {', '.join(selected_values)}"
if __name__ == "__main__":
app.run_server(debug=False)
This is the typical use case for leveraging pattern-matching callback selectors.
The pattern-matching callback selectors
MATCH
,ALL
, &ALLSMALLER
allow you to write callbacks that respond to or update an arbitrary or dynamic number of components.
The idea is to use composite id's (type+index) using dictionaries rather than strings, so that we can identify a given component as being the nth component of a specific type.
I also updated the first callback so that it makes partial property updates rather than sending back and forth all data across the network, which makes it more efficient.
from dash import Dash, dcc, html, Input, Output, ALL, Patch, callback, no_update
import dash_bootstrap_components as dbc
app = Dash(__name__)
app.layout = html.Div([
html.Button("Add Dropdown and Number Field", id="add-button"),
html.Div(id="input-container", children=[]),
html.Div(id="output"),
])
@app.callback(
Output("input-container", "children"),
Input("add-button", "n_clicks")
)
def add_input(n_clicks):
if n_clicks is None:
return no_update
patched_children = Patch()
new_input = dbc.Row([
dbc.Col(dcc.Dropdown(
id={'type': 'dropdown', 'index': n_clicks},
options=[
{'label': 'Option 1', 'value': 'option-1'},
{'label': 'Option 2', 'value': 'option-2'},
# Add more dropdown options as needed
],
value='option-1',
)),
dbc.Col(dcc.Input(
id={'type': 'weight', 'index': n_clicks},
type='number',
value=0,
)),
])
patched_children.append(new_input)
return patched_children
@callback(
Output("output", "children"),
Input({"type": "dropdown", "index": ALL}, "value"),
)
def process_dropdowns(values):
return html.Div(
['Selected Dropdown Values:'] +
[html.Div(f"Dropdown {i + 1} = {value}") for (i, value) in enumerate(values)]
)
if __name__ == "__main__":
app.run_server(debug=False)