Bring Drag & Drop to Dash with Dashboard Engine. 💫 Learn how at our next webinar!

Disable buttons while running callback using new dash version

Hi, I am executing a time-intensive query inside of a callback when clicking a button. During this, I am showing a loading symbol. Additionally, I also want to disable some buttons during this action. I tried with the following code, but with the new dash version it seems to not work.

    @app.callback(
        [Output('first-button', 'disabled'),
         Output('second-button', 'disabled'),
         Output('third-button', 'disabled')],
        [Input('start-process-button', 'n_clicks'),
         Input('process-finished-ack', 'children')]
    )
    def lock_buttons(n_clicks, end_ack):
        context = dash.callback_context.triggered[0]['prop_id'].split('.')[0]

        if context == 'start-process-button':
            if n_clicks > 0:
                return True, True, True
            else:
                return False, False, False
        else:
            return False, False, False

    @app.callback(
        Output('process-finished-ack', 'children'),
        [Input('start-process-button', 'n_clicks')]
    )
    def perform_process(n_clicks):
        # do something
        return "done"

How can I achieve this using the new dash version? I also tried to put loading symbols for all buttons, but it seems like it is not possible to trigger multiple loading elements as outputs. Are there any alternatives to disable buttons while running a callback?

Is this really not possible right now?

That should work. What error do you get? And what Dash version are you using?

I am using the newest Dash Version:

Name: dash
Version: 1.14.0

I don’t get any error message, the “lock_buttons”-function is just not being triggered. This bug is also discussed here: Button disable/enable while performing action

Well, you can do it using the nested trigger component trick. Here is an example,

import time
import dash
import dash_html_components as html
import dash_core_components as dcc

from dash_extensions.enrich import Dash, Output, Trigger

app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
    html.Button("Start", id="start"),
    html.Div(id="log", children=[dcc.Store(id="trigger")]),
])


@app.callback(Output("start", "disabled"), Trigger("start", "n_clicks"), Trigger("trigger", "data"))
def disable_buttons():
    context = dash.callback_context.triggered[0]['prop_id'].split('.')[0]
    return context == 'start'


@app.callback(Output("log", "children"), Trigger("start", "n_clicks"))
def long_task():
    time.sleep(2)
    return ["Task completed", dcc.Store(id="trigger")]


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

However, i would recommend simply running the job asynchronously and pulling updates using an Interval component. In this setup, you should avoid the issue you are seeing altogether.

1 Like