Hello,
I’m trying to use the Dash Mantine Accordion component to create a table where each row can be expanded to show more detail for the selected row. Each table row is a Mantine AccordionControl
and the detail is an AccordionPanel
. The detail content is potentially complex and will depend on the content of the row being selected. Since the detail content might be time consuming to load and generate, I want to update the AccordionPanel
content only when the AccordionControl
is expanded.
My thought was to use pattern matching callbacks to achieve this. Unfortunately, there’s no n_clicks
or equivalent property that I can find for the AccordionControl
or AccordionItem
, so the only solution I’m able to get working is to use the Accordion component’s value
property. The issue I’m running into is that this seems to be very slow - for some reason it takes a very long time for the result to display after the callback that updates the accordion panels completes.
Here’s a minimal example:
import dash
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
from dash import dcc, html
from dash.dependencies import Input, Output, State, ALL, MATCH
def accordion_table(**kwargs):
content = html.Div(
[
dcc.Loading(
[
html.Div(
[
dbc.Row(
[
dbc.Col('SERIAL_NUM'),
dbc.Col('column_1'),
dbc.Col('column_2'),
dbc.Col('column_3'),
dbc.Col('column_4'),
]
),
]
),
html.Div(
[
dmc.AccordionMultiple(id='accordion'),
]
)
]
),
]
)
@dash.callback(
[
Output('accordion', 'children'),
Output('accordion', 'value'),
],
[
Input(kwargs.get('serial_number_checklist_id'), 'value')
]
)
def update_accordion(serial_numbers):
if len(serial_numbers) == 0:
return list()
df = get_df(serial_numbers)
accordion_items = [
dmc.AccordionItem(
[
dmc.AccordionControl(
[
dbc.Row(
[
dbc.Col(row['SERIAL_NUM']),
dbc.Col(row['column_1']),
dbc.Col(row['column_2']),
dbc.Col(row['column_3']),
dbc.Col(row['column_4']),
]
)
],
id={'type': 'accordion_control', 'index': index}
),
dmc.AccordionPanel(id={'type': 'accordion_panel', 'index': index}),
],
id={'type': 'accordion_item', 'index': row['SERIAL_NUM']},
value=row['SERIAL_NUM']
)
for index, row in df.iterrows()
]
return accordion_items, []
@dash.callback(
Output({'type': 'accordion_panel', 'index': ALL}, 'children'),
[
Input('accordion', 'value')
],
[
State({'type': 'accordion_item', 'index': ALL}, 'value')
]
)
def accordion_value_test(values, item_states):
if len(values) == 0:
return dash.no_update
return_values = [''] * len(item_states)
for v in values:
index = item_states.index(v)
return_values[index] = 'Detail content goes here'
return return_values
return content
The first callback creates all the accordion items based on dataframe content. I’ve included the accordion’s value
property in the output because I want to collapse all rows when a new dataframe is loaded.
The second callback updates the accordion panel content when the accordion’s value changes. The input is basically a list of serial numbers corresponding to the expanded rows and a list of serial numbers for all rows in the accordion. The state is used to figure out which accordion panel needs content updated - all other panels are just passed an empty string.
This works, but it seems unreasonably slow. When I use a debugger to try to see what’s going on, the 2nd callback completes, then the loading component will continue to spin for almost 30 seconds before the table is displayed again.
Any help or alternative solutions are very much appreciated!