首页 > 解决方案 > Plotly Dash:删除元素后自动触发下载

问题描述

在此处输入图像描述

如您所见,如果我按下下载按钮,下载弹出窗口就会出现。下载后,如果我尝试删除其他卡,下载弹出窗口再次出现。为什么会这样?

这是一个最小的可重现示例

重现步骤

  1. 按下载按钮并下载或取消您喜欢的任何内容
  2. 现在尝试通过按删除按钮删除其他卡

观察: 下载弹窗应该又来了

import json

import dash
import dash_bootstrap_components as dbc
from dash import dcc
from dash.dependencies import Input, Output, ALL, State

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = dbc.Container([
    dbc.Row([
        dcc.Download(id="download-1"),
        'card 1',
        dbc.Col(dbc.Button('download', id='download-btn-1')),
        dbc.Col(dbc.Button('delete', id={'type': 'delete', 'id': 'card-1'}))
    ], className='bg-warning m-2 p-2', id='card-1'),
    dbc.Row([
        dcc.Download(id="download-2"),
        'card 2',
        dbc.Col(dbc.Button('download', id='download-btn-2')),
        dbc.Col(dbc.Button('delete', id={'type': 'delete', 'id': 'card-2'}))
    ], className='bg-warning m-2 p-2', id='card-2'),
    dbc.Row([
        dcc.Download(id="download-3"),
        'card 3',
        dbc.Col(dbc.Button('download', id='download-btn-3')),
        dbc.Col(dbc.Button('delete', id={'type': 'delete', 'id': 'card-3'}))
    ], className='bg-warning m-2 p-2', id='card-3'),
], id='container-body')


@app.callback(
    Output('download-1', 'data'),
    Input('download-btn-1', 'n_clicks'),
    prevent_initial_call=True
)
def download_1(n_clicks):
    return dict(content="data 1", filename="hello.txt")


@app.callback(
    Output('download-2', 'data'),
    Input('download-btn-2', 'n_clicks'),
    prevent_initial_call=True
)
def download_1(n_clicks):
    return dict(content="data 2", filename="hello.txt")


@app.callback(
    Output('download-3', 'data'),
    Input('download-btn-3', 'n_clicks'),
    prevent_initial_call=True
)
def download_1(n_clicks):
    return dict(content="data 3", filename="hello.txt")


@app.callback(
    Output('container-body', 'children'),
    Input({'type': 'delete', 'id': ALL}, 'n_clicks'),
    State('container-body', 'children'),
    prevent_initial_call=True
)
def delete_children(n_clicks, children):
    card_id_to_be_deleted = json.loads(dash.callback_context.triggered[0]['prop_id'].split('.')[0])['id']
    index_to_be_deleted = None
    for index, c in enumerate(children):
        if c['props']['id'] == card_id_to_be_deleted:
            index_to_be_deleted = index
            break
    children.pop(index_to_be_deleted)
    return children


if __name__ == '__main__':
    app.run_server(debug=True)

标签: pythonplotly-dash

解决方案


除了其他答案之外,还提供更多上下文。

我认为正在发生的事情是您的delete_children回调导致Download组件重新安装并因此更新。

下载组件使用componentDidUpdateReact 生命周期方法。在componentDidMount它内部检查是否data不是null或是否data等于 的先前值data。如果不是这样,则触发保存对话框(source)。

单击第一个下载按钮后,data第一个下载组件的属性值不为空,并且与之前的状态(空)不同。因此,当delete_children触发回调时,componentDidUpdate会调用下载组件,下载组件会检测到data已更改且不为空,因此会触发保存对话框。

因此,并不是在您的delete_children回调之后触发了您的下载回调(您可以通过在每个下载回调中记录一些内容来检查这是否没有发生)。这是下载组件的更新,其中一些组件检测到它们应该根据它们的data属性值触发保存。

您还会注意到,如果您按下载按钮 1,然后按下载按钮 2,然后按删除按钮 3,则会依次弹出两个保存对话框。两个下载组件的值发生data了变化,并且调用了下载组件的componentDidMount方法,因此出现了两个保存对话框。


推荐阅读