
从这里你可以看到,如果我按下下载按钮,下载弹出就来了。下载后,如果我尝试删除其他卡,下载弹出再次出现。为什么会这样?
下面是一个最小的可重现性示例
复制的步骤
观察:下载弹出应该再次出现
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)发布于 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方法,因此出现了两个保存对话框。
发布于 2021-11-16 04:11:16
另一种解决方案可以是使用clientside_callback和客户端回调上下文。
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)https://stackoverflow.com/questions/69974212
复制相似问题