I want to make a button that adds a slider every time. This code works for adding exactly one slider and then just updates that one. Can someone help me modify this example? I need more sliders!!!
import dash
from dash.dependencies import State, Event, Input, Output
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
app = dash.Dash()
app.layout = html.Div([
html.H1('MORE SLIDERS!!!'),
html.Button('Make One More', id='my-button-events-example'),
html.Div(id='counter'),
html.Div(id='0'),
dcc.Slider(
min=0,
max=90,
value=np.random.randint(0,90),
)
])
@app.callback(
Output(component_id='counter', component_property='children'),
events=[Event('my-button-events-example', 'click')],
)
def test():
return dcc.Slider(
min=0,
max=90,
value=np.random.randint(0,90),
)
if __name__ == '__main__':
app.run_server(debug=True)
You can to add sliders to a children property of one of your Divs (in a loop, if that is what you need).
The only complication is that you need to predefine all Callbacks for all slides you are ever going to create.
Tip: you can create callbacks programatically, if you want to create callbacks in a loop too. But, again, this should be executed as part of initialisation, not inside another callback.
Note that the n_clicks property has been added to all of the html components, so you should use this property instead of the Events now:
import dash_html_components as html
app.layout = html.Div([
html.Button('Add a slider', id='button', n_clicks=0),
html.Div(id='slider-container'),
])
@app.callback(Output('slider-container', 'children'), [Input('button', 'n_clicks')])
def add_sliders(n_clicks):
return html.Div(
[dcc.Slider(id='slider-{}'.format(i)) for i in range(n_clicks)] +
[html.Div(id='output-{}'.format(i)) for i in range(n_clicks)]
)
# up to 10 sliders
for i in range(10):
@app.callback(Output('slider-{}'.format(i), 'children'), [Input('slider-{}'.format(i), 'value')])
def update_output(slider_i_value_):
return slider_i_vlaue
There are a few limitations with this approach right now:
You have to define all of your callbacks in advance. If your UI can support 100 sliders, then you need to define 100 callbacks
A single callbackâs Output will only work if all of the Inputs are rendered. So, you canât have an output element that depends on a variable number of visible Input elements.
This behaviour will likely update in the future and allow for more sophisticated dynamic interfaces. Itâs helpful to know what types of interfaces users are attempting to design, so thanks for posting this issue. Any more info on what youâre trying to build and why would be super helpful
thanks for the help! unfortunately iâm getting an error running your example:
Traceback (most recent call last):
File âmany_sliders.pyâ, line 23, in @app.callback(Output(âslider-{}â.format(i), âchildrenâ), [Input(âslider-{}â.format(i), âvalueâ)])
File â/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/dash/dash.pyâ, line 528, in callback
self._validate_callback(output, inputs, state, events)
File â/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/dash/dash.pyâ, line 441, in _validate_callback
).replace(â ', ââ))
dash.exceptions.NonExistantIdException:
Attempting to assign a callback to the
component with the id âslider-0â but no
components with id âslider-0â exist in the
appâs layout.
Here is a list of IDs in layout:
[âbuttonâ, âslider-containerâ]
If you are assigning callbacks to components
that are generated by other callbacks
(and therefore not in the initial layout), then
you can suppress this exception by setting app.config.supress_callback_exceptions=True.
I made sure to update dash and all of the components before runningâŚ
Ah yeah, sorry, I left out that part. You need to add:
app.config.supress_callback_exceptions=True
to make it work. The exception printed that out:
but thatâs a little confusing.
We throw an exception if an ID wasnât found in the initial app.layout because itâs easy to make a typo and mispell an ID. In this case, weâre generating elements directly (not in the layout) but defining our callbacks all upfront. Dash throws an exception in case that we mispelled something. In this case we didnât, so we can just supress the exception.
Ah yeah, Dash is picking up the correct JS and CSS bundles. Hereâs an updated example:
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
app = dash.Dash()
app.config.supress_callback_exceptions = True
app.layout = html.Div([
html.Button('Add a slider', id='button', n_clicks=0),
html.Div(id='slider-container'),
# this is a hack: include a hidden dcc component so that
# dash registers and serve's this component's JS and CSS
# libraries
dcc.Input(style={'display': 'none'})
])
@app.callback(Output('slider-container', 'children'), [Input('button', 'n_clicks')])
def add_sliders(n_clicks):
return html.Div(
[html.Div([
html.Div(dcc.Slider(id='slider-{}'.format(i))),
html.Div(id='output-{}'.format(i), style={'marginTop': 30})
]) for i in range(n_clicks)]
)
# up to 10 sliders
for i in range(10):
@app.callback(Output('slider-{}'.format(i), 'children'), [Input('slider-{}'.format(i), 'value')])
def update_output(slider_i_value):
return slider_i_value
if __name__ == '__main__':
app.run_server(debug=True)
I happened to scroll back to this post and now Iâm wondering if this is still in the plans. The types of interfaces I have in mind are:
user loads some data from a file or database
for each item in the data, app creates an interactive component. Say, a graph + a dropdown that controls something about the graph.
Right now the easiest way to accomplish this seems to me to be a fixed maximum number of items, and then unhide the relevant number when the data is loaded. Are there better ideas?
I have a similar issue, in that our application have no upper limit on how many UI elements a user can add. If callbacks can be generated automatically (in a loop) that would be really really awesome.