Hello jcthomas,
Thanks for your workaround. I was trying to use it with a graph, but I cannot get it working. It seems to look allright, but after selecting data in the graph and using the dropdown menu to deselect the data, the dropdown menu is getting empty.
I think I have to keep/save the selected data somehow, but I don’t know how exactly.
Any idea’s? Or is there someone who made this working with a graph?
Thanks in advance!
import dash
from dash.exceptions import PreventUpdate
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.express as px
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.DataFrame({'City': ['New York City', 'Boston', 'San Francisco'], 'Value': [100, 200, 300]})
# create a layout with a multi-select dropdown and a graph
def get_dropdown(value=None):
value = [] if value is None else value
return html.Div(
[dcc.Dropdown(
id='dropdown',
options=[
{'label': 'New York City', 'value': 'New York City'},
{'label': 'Boston', 'value': 'Boston'},
{'label': 'San Francisco', 'value': 'San Francisco'}
],
multi=True,
value=value
)],
id='dropdown-container',
)
def create_graph(value=None):
value = [] if value is None else value
df_filter = df[df['City'].isin(value)]
figure = px.bar(df_filter, x='City', y='Value')
figure.update_layout(clickmode='event+select')
return html.Div([
dcc.Graph(
id='graph',
figure=figure,
# selectedData={'New York City': 100, 'Boston': 200, 'San Francisco': 300}
)
], id='graph-container')
app.layout = html.Div([
get_dropdown(),
create_graph(),
html.Div([], id='previously-selected'),
html.Div([html.Pre(id='click-data')])
])
# Callback one recreates the graph using new values
# and updates the previously-selected value
@app.callback(
[Output('dropdown-container', 'children'), Output('previously-selected', 'children')],
[Input('graph', 'selectedData')],
[State('previously-selected', 'children')]
)
def update_via_children(value, prev_selected):
print('callback one')
if value is not None:
value = [i.get('label') for i in value.get('points', {})]
else:
value = []
if sorted(value) == sorted(prev_selected):
raise PreventUpdate
return get_dropdown(value=value), value
# Callback two updates the value of dropdown directly and so could be used to alter something
# complicated like a Graph, hopefully. Does not update previously-selected, so callback one
# will be called again, recreating graph, triggering callback one a second time, but
# this time previously-selected == value
@app.callback(
Output('graph-container', 'children'),
[Input('dropdown', 'value')],
[State('previously-selected', 'children')]
)
def update_directly(value, prev_selected):
print('callback two')
if sorted(value) == sorted(prev_selected):
raise PreventUpdate
return create_graph(value=value)
app.run_server(debug=True)