2
votes

I am using python dash and want to create a list of menus/forms that can be extended and reduced dynamically by clicking buttons to do so. Adding new forms/menus should add another identical form to the page (a list of forms/menus).

The following code allows the addition/removal of additional divs enclosing multiple dash core components, however, whenever I choose an option in one of the dropdowns or enter anything into one of the input fields, what I've chosen or entered disappears again.

import dash
import dash_core_components as dcc
import dash_html_components as html

step = html.Div(
        children=[
            "Menu:",
            dcc.Dropdown(options=[{'label': v, 'value': v} for v in ['option1', 'option2', 'option3']]),
            dcc.Input(placeholder="Enter a value ...", type='text', value='')
        ])

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

div_list = [step]

app.layout = html.Div(
    children=[
        html.H1(children='Hello Dash'),
        html.Div(children=div_list, id='step_list'),
        html.Button('Add Step', id='add_step_button', n_clicks_timestamp='0'),
        html.Button('Remove Step', id='remove_step_button', n_clicks_timestamp='0')])


@app.callback(
    dash.dependencies.Output('step_list', 'children'),
    [dash.dependencies.Input('add_step_button', 'n_clicks_timestamp'),
     dash.dependencies.Input('remove_step_button', 'n_clicks_timestamp')],
    [dash.dependencies.State('step_list', 'children')])
def add_step(add_ts, remove_ts, div_list):
    add_ts = int(add_ts)
    remove_ts = int(remove_ts)
    if add_ts > 0 and add_ts > remove_ts:
        div_list += [step]
    if len(div_list) > 1 and remove_ts > add_ts:
        div_list = div_list[:-1]
    return div_list


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

Can anybody explain to me what I’m doing wrong?

Thanks a lot!

1

1 Answers

1
votes

Dash components need to have ids specified in order to save values after callbacks.

This example generating step as a function using random ids fixes the problem:

import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np

def step():
     return html.Div(
        children=[
            "Menu:",
            dcc.Dropdown(options=[{'label': v, 'value': v} for v in ['option1', 'option2', 'option3']],id=str(np.random.randn())),
            dcc.Input(placeholder="Enter a value ...",id=str(np.random.randn()))
        ])

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(
    children=[
        html.H1(children='Hello Dash'),
        html.Div(children=[step()], id='step_list'),
        html.Button('Add Step', id='add_step_button', n_clicks_timestamp=0),
        html.Button('Remove Step', id='remove_step_button', n_clicks_timestamp=0)])

@app.callback(
    dash.dependencies.Output('step_list', 'children'),
    [dash.dependencies.Input('add_step_button', 'n_clicks_timestamp'),
     dash.dependencies.Input('remove_step_button', 'n_clicks_timestamp')],
    [dash.dependencies.State('step_list', 'children')])
def add_step(add_ts, remove_ts, div_list):
    add_ts = int(add_ts)
    remove_ts = int(remove_ts)
    if add_ts > 0 and add_ts > remove_ts:
        div_list += [step()]
    if len(div_list) > 1 and remove_ts > add_ts:
        div_list = div_list[:-1]
    return div_list

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