Dashboard idea (dash-2.7.1):
I have one variable for slider (‘Average MW Per Plant’), and another variable (‘State’) for checklist. A datatable will be updated upon user interaction.
Three (logical) steps of user interaction:
- Users set the slider variable first (now working fine);
- Checklist options will get updated based on the filtered dataframe generated by Step 1, and all checklist values will be selected by default (now working fine);
- Users select/unselect the values among the updated checklist options generated by Step 2 (now problematic);
Symptom in Step 3:
When user unselects a checklist value, it is selected back automatically (but somehow the datatable filtering is still working from what it is displayed).
More details:
In my first callback, only the checklist options are updated, not the checklist values. In my second callback, I explicitly tell Dash that only after the slider is moved (not when checklist is selected/unselected), all values will be selected. Anyone has an idea what the problems could be?
Minimal reproducible example:
from dash import Dash, Input, Output, dash_table
from dash import dcc, html
from dash.dash import no_update
import pandas as pd
import dash_bootstrap_components as dbc
app = Dash(__name__, external_stylesheets=[dbc.themes.LITERA])
server = app.server
df = pd.read_csv('https://git.io/Juf1t')
# initialize a filtered dataframe when dashboard is first loaded
# filtering is based on 'Average MW Per Plant' (a variable used for slider defined below)
df_filtered = df.loc[df['Average MW Per Plant'].between(10.0, 23.0, inclusive='both'), df.columns]
# Create state list for checklists based on the complete data
# This list is used every time slider is moved so that all values of checklist are selected
complete_states = sorted(df['State'].unique())
# Create state list for checklists based on the initial slider-filtered dataframe
chlst = sorted(df_filtered['State'].unique())
app.layout = dbc.Container([
html.Hr(),
dbc.Row([
dbc.Label("Set a range of Average MW Per Plant:"),
dcc.RangeSlider( # slider
id='amw-slider',
min=4.0,
max=23.0,
value=[10.0, 23.0]
),
html.Hr(),
dcc.Checklist( # checklist of states
id='chlst',
options=[{'label':x, 'value':x} for x in chlst],
value=chlst,
labelClassName="mr-3"
),
html.Hr(),
dash_table.DataTable(df_filtered.to_dict('records'),
columns=([{"name": i, "id": i} for i in df_filtered.columns]),
id='tbl')
])
])
# first callback
@app.callback(Output('tbl', 'data'),
Output('chlst', 'options'),
Input('amw-slider', 'value'),
Input('chlst', 'value'))
def update_table(amw, chlst):
# filter dataframe based on user interaction with slider
filtered_df = df.loc[df['Average MW Per Plant'].between(amw[0], amw[1], inclusive='both'), df.columns]
# get the updated state list based on user interaction with slider
filtered_chlst = sorted(filtered_df['State'].unique().tolist())
# further filter dataframe based on user interaction with checklist
for idx, x in filtered_df.iterrows():
if x['State'] not in set(chlst):
filtered_df.drop(idx, inplace=True)
return filtered_df.to_dict('records'), filtered_chlst
# second callback that resets checklist to "select all" once slider is moved
@app.callback(
Output('chlst', 'value'),
Input('amw-slider', 'value')
)
def check_all(amw):
if not amw:
return no_update
else:
return complete_states
if __name__ == "__main__":
app.run_server(debug=True)