How to dynamically create dropdowns?

Hello,

I’m trying to create a Dash app in which you first choose a model type and then model settings that are dynamic, based on the model type. I have seen other topics posted here about dynamically creating a layout, but none seem to show a dropdown being dynamically created, conditionally. Eg:

https://community.plotly.com/t/dynamically-declaration-of-dash-callback/5696/5
https://community.plotly.com/t/clear-explanation-of-dynamic-callback-generation/21992/2
and on github:
https://github.com/plotly/dash/issues/475

Does anyone know how this can be done? See full example (that fails) below. This is a minimal example with 2 models, “OLS” and “Lasso”. If selecting OLS, there is one model setting (polynomial_order) and if Lasso, there are two model settings to choose (polynomial_order and alpha).

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

app = dash.Dash()
server = app.server
app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    dcc.Tabs(id='modeltype-selector',value='OLS',
             children=[dcc.Tab(label=x, value=x) for x in ['OLS','Lasso']]),
    html.Div(id='model-settings'),
    html.Div(id='graph')
])

@app.callback(Output('model-settings', 'children'), [Input('model-selector', 'value')])
def define_settings(modeltype):
    settings = [dcc.Input(id="polynomial_order", type="number", placeholder="0", debounce=True)]
    if modeltype == 'Lasso':
        settings.append([dcc.Input(id="alpha", type="number", placeholder="0", debounce=True)])
    return settings

@app.callback(Output('graph', 'children'), [Input('model-settings', 'children')])
def graph(settings):
    # **** generate figure here based on model settings
    return html.Div([dcc.Graph(id='graph-1',figure={'data': [{'x': [1],'y': [1]}]})])

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

Thanks!

Creating a layout dynamically is easy. Creating callbacks dynamically is impossible (at the moment!).

The workaround is to define your full layout up front. Connect all your callbacks and then add some callbacks to hide/show UI elements as required.

This should do the trick:

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

app = dash.Dash()
server = app.server

app.layout = html.Div([

    dcc.Dropdown(
        id='model-selector',
        options=[{'label': i, 'value': i} for i in ["OLS", "Lasso"]],
        value="OLS"
    ),

    dcc.Input(id="polynomial-order", type="number", value=0),
    dcc.Input(id="alpha", type="number", value=0),

    html.Div(id='display-selected-values')
])

# HIDE THE ALPHA SELECTOR (YOU COULD HIDE AN ENTIRE DIV IF YOU WANTED)
@app.callback(Output('alpha', 'style'), [Input('model-selector', 'value')])
def toggle_container(toggle_value):
    if toggle_value == 'OLS':
        return {'display': 'none'}
    else:
        return {'display': 'block'}


@app.callback(
    Output('display-selected-values', 'children'),
    [Input('model-selector', 'value'),
     Input('polynomial-order', 'value'),
     Input('alpha', 'value')])
def set_display_children(model, order, alpha):
    if model == "OLS":
        return f"Model: {model},  Order: {order}."
    else:
        return f"Model: {model},  Order: {order}, Alpha: {alpha}."