I am writing a small Dash app that will allow you to upload a csv, then manipulate column types. One of the features is to "ignore" a specific column. The end-state would be that the "ignored" column will be dropped from the later-previewed data table.
Since I am assuming I do NOT know the number of columns when the file is uploaded, I have built a callback that creates a table based on the number of columns in the input file.
# SECTION: PARSING UPLOAD DATA
@app.callback(Output('data_picker_table', 'children'),
Input('hidden_df', 'children'))
def parse_file(hidden_df):
if not hidden_df:
raise dash.exceptions.PreventUpdate
else:
df = pd.read_json(hidden_df, orient='split')
cols = df.columns.tolist()
tbl_styles = {'verticalAlign': 'middle', 'textAlign': 'center'}
headers = [
html.Thead(html.Tr([html.Th('Column', style=tbl_styles), html.Th('Casted Type', style=tbl_styles),
html.Th('Ignore', style=tbl_styles)]))]
id = 0
for col in cols:
# print(df[col].dtype)
if df[col].dtype == 'datetime64[ns]':
dropdown = dbc.Select(id={'type': 'dtype_dropdown', 'index': id}, placeholder='Casted dtype',
options=[{'label': type, 'value': type} for type in
['datetime64', 'object/string']],
className='custom-select')
toggle = dbc.Checklist(id={'type': 'column_toggle', 'index': id},
options=[{'label': 'Drop', 'value': 1}],
switch=True)
headers.append(
html.Tr(id={'type': 'data_row', 'index': id},
children=[html.Td(col, style=tbl_styles, id={'type': 'col_name', 'index': id}),
html.Td(dropdown, style=tbl_styles),
html.Td(toggle, style=tbl_styles)]))
id += 1
table = dbc.Table(headers, striped=True, bordered=True, className='table-secondary')
return table
You'll see that each row of the table appended to the headers list contains a column name, dropdown, and a toggle switch.
Now when I go to test that the toggle switches work with the below callback, I would expect that this would ingest EVERY 'column_toggle', but it seems to only care about the first toggle in row one.
@app.callback(Output({'type': 'printer', 'index': MATCH}, 'children'),
Input({'type': 'column_toggle', 'index': MATCH}, 'value'),
State({'type': 'col_name', 'index': MATCH}, 'children'))
def print_dropped(value, col):
if value and value[0] == 1:
print(value, col)
return ''
Even when playing in debug mode, the breakpoint for value[0] == 1 provides nothing unless the first toggle is activated. When the first toggle is hit, the debug output shows the following.
col = {str} 'Date'
value = {list: 1} [1]
0 = {int} 1
__len__ = {int} 1
EDIT -- I have looked into the dash.callback_context information to debug further and have found that the inputs_list seems to confirm that only one of the column_toggle IDs is being picked up.
"in_list": [
{
"id": "button",
"property": "n_clicks",
"value": 4
},
{
"id": {
"index": 0,
"type": "column_toggle"
},
"property": "value",
"value": false
}
]
Any guidance on what key error I am making in this design would be greatly appreciated. From what I have found, what I am trying to do should fall nicely into the dynamic callback functionality I would think.