Hey everyone! I was just trying to play around with the AIO components to make an opinionated Excel Download button that I could use across all of the pages in my Dash application. The construction would be something like the following:
class ExcelDownloadAIO(html.Div): # html.Div is the parent element
# :param df: Pandas DataFrame to be written in Excel (or eventually a dictionary
# that maps out the hierarchy, placement, and formatting of the file)
# :param filename: Default filename for Excel file
# :param filename_input_props: (Optional) Override default arguments for filename_input
# :param download_button_props: (Optional) Override default arguments for download_button
# :param download_component_props: (Optional) Override default arguments for download_component
# Set of functions that create pattern-matching callbacks of the subcomponents
class ids:
filename_input = lambda aio_id: {
"component": "ExcelDownloadAIO",
"subcomponent": "filename_input",
"aio_id": aio_id,
} # dbc.Input component
download_button = lambda aio_id: {
"component": "ExcelDownloadAIO",
"subcomponent": "download_button",
"aio_id": aio_id,
} # dbc.Button component
download_component = lambda aio_id: {
"component": "ExcelDownloadAIO",
"subcomponent": "download_component",
"aio_id": aio_id,
} # dcc.Download component
status = lambda aio_id: {
"component": "ExcelDownloadAIO",
"subcomponent": "status",
"aio_id": aio_id,
} # dcc.Store component
# Make the ids class a public class
ids = ids
# Parameters to pass into the ExcelAIO Component
def __init__(
self,
data, # Pandas DataFrame (eventually full dictionary mapping)
filename_props={},
download_button_props={},
download_component_props={},
status_props={},
aio_id={},
):
# Create a unique AIO id if one is not passed
aio_id = aio_id if aio_id else str(uuid.uuid4())
self.data = data
# Create / reconcile default parameters with user inputted values
filename_props_user = filename_props.copy()
filename_props.update({
"placeholder": "Filename",
"debounce": True,
"type": "text",
})
filename_props.update(filename_props_user)
download_button_props = (
download_button_props.copy() if download_button_props else {}
)
if "size" not in download_button_props:
download_button_props["size"] = "sm"
if "className" not in download_button_props:
download_button_props["className"] = "btn-secondary"
download_component_props = (
download_component_props.copy() if download_component_props else {}
)
super().__init__(
[ # Equivalent to `html.Div([...])`
dbc.Row(
dbc.Form(
[
dbc.InputGroup(
[
dbc.Input(
id=self.ids.filename_input(aio_id),
**filename_props
),
dbc.InputGroupText(".xlsx"),
],
size="sm",
className="mb-3",
),
html.Div(
dbc.Button(
"Download",
id=self.ids.download_button(aio_id),
**download_button_props
),
className="d-grid",
),
dcc.Download(
id=self.ids.download_component(aio_id),
), # **download_component_props)
dcc.Store(id=self.ids.status(aio_id), **status_props),
],
),
className="d-flex justify-content-center",
),
]
)
# Handles filename validation on form submission:
# @callback(
# Output(),
# Input(),
# Input(),
# )
# Handles download callback:
@callback(
Output(ids.status(MATCH), "data"),
Input(ids.download_button(MATCH), "n_clicks"),
State(ids.filename_input(MATCH), "value"),
prevent_initial_call=True,
)
def xlsx_download(n_clicks, filename):
temp_file = NamedTemporaryFile()
# with pd.ExcelWriter(temp_file, engine="xlsxwriter") as writer:
return dcc.send_file(temp_file.name)
Now, my question is that when I would use this AIO in the layout of the other app, what I envisioned was something along these lines: ExcelDownloadAIO(data=df)
, but I don’t quite know how to access the df
Pandas dataframe in the xlsx_download
callback? Thanks!