首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实际上是Dash:删除元素后自动触发下载

实际上是Dash:删除元素后自动触发下载
EN

Stack Overflow用户
提问于 2021-11-15 12:10:35
回答 2查看 1K关注 0票数 1

从这里你可以看到,如果我按下下载按钮,下载弹出就来了。下载后,如果我尝试删除其他卡,下载弹出再次出现。为什么会这样?

下面是一个最小的可重现性示例

复制的步骤

  1. 按“下载”按钮,下载或取消任何您喜欢的内容。
  2. 现在按下“删除”按钮,尝试删除其他卡。

观察:下载弹出应该再次出现

代码语言:javascript
复制
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)
EN

回答 2

Stack Overflow用户

发布于 2021-11-16 07:46:35

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

我认为所发生的事情是,您的delete_children回调会导致Download组件重新坐骑,从而更新。

下载组件使用componentDidUpdate React生命周期方法。在componentDidMount内部,它检查data是否不是null,或者data是否等于data的前一个值。如果这不是真,则触发保存对话框(来源)。

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

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

您还会注意到,如果您按下下载按钮1,然后下载按钮2,然后删除按钮3,两个保存对话框将弹出后,相接。两个下载组件的data值已经更改,并且调用了下载组件的componentDidMount方法,因此出现了两个保存对话框。

票数 2
EN

Stack Overflow用户

发布于 2021-11-16 04:11:16

另一种解决方案可以是使用clientside_callback和客户端回调上下文。

代码语言:javascript
复制
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):
    if n_clicks is not None:
        return dict(content="data 1", filename="hello1.txt")


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


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


app.clientside_callback(
    """
    function(n_clicks, children) {
        const triggered = dash_clientside.callback_context.triggered.map(t => t.prop_id);
        const card_id_to_remove = JSON.parse(triggered[0].split('.')[0])['type'];
        let child_index_to_remove = null;
        for(let i=0; i<children.length; i++){
            if (children[i]['props']['id'] === card_id_to_remove){
                child_index_to_remove = i;
                break;
            }
        }
        children.splice(child_index_to_remove, 1);
        return children;
    }
    """,
    Output('container-body', 'children'),
    Input({'type': 'delete', 'id': ALL}, 'n_clicks'),
    State('container-body', 'children'),
    prevent_initial_call=True
)

if __name__ == '__main__':
    app.run_server(debug=True)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69974212

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档