I have created a dynamic number of dropdowns based on the columns of a dataframe selected by the user. Then I need to modify the options of every dropdown based on the input change of any of then.
When using ALL in the output I was hopping to match all the dropdowns and return a list of new options to all items, however the output doesn’t update any of then.
Here is a summarized version of the code:
Define the layout
app.layout = html.Div([
html.Div([
html.Div( [
html.Label('Simulation'),
dcc.Dropdown(
id='simulation',
options=[{'label': i, 'value': i} for i in hm.lib.list_symbols()],
placeholder="Select a dataframe",
),
html.Div(
id='both_axis',
children = [],
),
html.Button('Reset',id='reset_button', n_clicks=0),
],
style={'width': '49%', 'display': 'inline-block'})
]),
html.Div(id = 'features', children = []),
html.Div([dcc.Graph(id= 'heatmap')
], style={'width': '80%'}), ])
Populate the id=features with dropdowns, after receving the dataframe name from id = simulation
@app.callback(
dash.dependencies.Output('features', 'children'),
[dash.dependencies.Input('simulation', 'value')],
[dash.dependencies.State('features', 'children')])
def display_dropdowns(simulation, children):
if simulation != None:
df = dict_of_df[simulation].copy()
cat_name, not_cat_name = _is_cat(df) #Function to identify categorical variables
dropdowns = []
for col in cat_name:
values_to_display = df[col].value_counts().index
dropdowns.append(dcc.Dropdown(id= {'type': 'features',
'index': col},
options=[{'label': i, 'value': i} if i not in [True, False] else {'label': str(i), 'value': str(i)} for i in values_to_display],
value = None,
placeholder=col))
return dropdowns
else:
raise dash.exceptions.PreventUpdate
Here is the problem:
For any change in dropdown value the callback is called and should return 2 lists (options and disabled) modifying every dropdown.
@app.callback([dash.dependencies.Output({'type': 'features', 'index': ALL}, 'options'),
dash.dependencies.Output({'type': 'features', 'index': ALL}, 'disabled')],
[dash.dependencies.Input({'type': 'features', 'index': ALL}, 'value'),
dash.dependencies.Input({'type': 'both_axis', 'index': 'y_axis'}, 'value'),
dash.dependencies.Input({'type': 'both_axis', 'index': 'x_axis'}, 'value')],
[dash.dependencies.State('simulation', 'value'),
dash.dependencies.State('reset_button', 'n_clicks')])
def _modify_df(features, y_axis, x_axis, simulation, reset_button):
ctx = dash.callback_context
if all(v is None for v in features):
raise dash.exceptions.PreventUpdate
else:
dl = dict_of_df[simulation].copy()
cat_name, not_cat_name = _is_cat(dl)
input_ = ctx.triggered[0]['value']
try:
trigger_loaded = json.loads((ctx.triggered[0]['prop_id']).split('.')[0])['index']
except:
trigger_loaded = ctx.triggered[0]['prop_id'].split('.')[0]
if input_ == 'False':
input_ = False
elif input_ == 'True':
input_ == True
if trigger_loaded not in ['x_axis', 'y_axis', 'reset_button', 'simulation']:
names_and_values.append([trigger_loaded, input_])
if trigger_loaded == 'reset_button':
names_and_values.clear()
for tupla in names_and_values:
if (tupla[0] != None) and (tupla[1] != None):
dl = dl[dl[tupla[0]] == tupla[1]]
for col in cat_name:
values_to_display_final = dl[col].value_counts().index
all_columns.append([{'label': i, 'value': i} if i not in [True, False] else {'label': str(i), 'value': str(i)} for i in values_to_display_final])
if col not in [y_axis, x_axis, reset_button, simulation]:
disabled.append(False)
elif col in [y_axis,x_axis]:
disabled.append(True)
return all_columns+disabled
Questions:
Does the ALL method works in the output, and if so, how the return should be?
I could create a list of outputs [dash.dependencies.Output(id1, ‘options’), dash.dependencies.Output(id2, ‘options’),…] inside a different callback using the columns of the selected dataframe, how can I share this list to be used as output?