Data-table broken when output div is wrapped in loading component

I have a dash app that creates various data-tables in an output div when the user clicks certain input buttons. It has worked fine until I wrapped the output div in a dcc.Loading component. After introducing the dcc.Loading component, I see a browser error and the buttons no longer function properly. A snippet of the broswer error is:

react-dom@16.8.6.min.js?v=1.1.0&m=1569998522:117 Error: An object was provided as children instead of a component, string, or number (or list of those). Check the children property that looks something like…

Here is a small reproducible code sample:

import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import time

app = dash.Dash(__name__)

g_dbg_ct = 0
def get_table():
    # create dummy table.
    global g_dbg_ct
    g_dbg_ct += 1
    test_columns = [{'name':'column1', "id":"column1"},
                    {'name':'column2', "id":"column2"},
                    {'name':'column3', "id":"column3"}]
    test_data = [ {'column1': g_dbg_ct, 'column2': 'montreal', 'column3': 'canada'}, {'column1': 8, 'column2': 'boston', 'column3': 'america'} ]
    tt_data_table = dash_table.DataTable(id='tt_data_table',
                                         columns=test_columns,
                                         data=test_data,
                                         editable=False)
    return tt_data_table

my_button_id1 = dbc.Button("Button1", id="my_button_id1")
my_button_id2 = dbc.Button("Button2", id="my_button_id2")
my_button_id3 = dbc.Button("Button3", id="my_button_id3")
output_div_id = html.Div(children=["My output div.", get_table()], id="output_div_id", style={"border":"solid red 2px"})


#
# If I wrap output_div_id in the loading component, this app breaks.
#
output_div_to_use = html.Div(dcc.Loading(id="loading1",
                                         children=[output_div_id],
                                         type="default",
                                         fullscreen=False))
app.layout = html.Div(children=[my_button_id1, my_button_id2, my_button_id3, output_div_to_use])


g_btn_ct = 0
@app.callback(Output('output_div_id', 'children'),
              [Input("my_button_id1", 'n_clicks'),
               Input("my_button_id2", 'n_clicks'),
               Input("my_button_id3", 'n_clicks')]
              )
def update_output(n_clicks1, n_clicks2, n_clicks3):
    ctx = dash.callback_context
    if not ctx.triggered:
        ret_id = None
    else:
        ret_id = ctx.triggered[0]['prop_id'].split('.')[0]

    if ret_id == None:
        # Return without updating web page.
        raise PreventUpdate

    global g_btn_ct
    import time

    if ret_id == "my_button_id1":
        output = "Button %s pressed. g_btn_ct=%d." % (ret_id, g_btn_ct)
        children = [output, get_table()]
        # simulate a slow callback so we can see the loading state spinner.
        time.sleep(2)
    elif ret_id == "my_button_id2":
        output = "Button %s pressed. g_btn_ct=%d." % (ret_id, g_btn_ct)
        children = [output]
    elif ret_id == "my_button_id3":
        output = "Button %s pressed. g_btn_ct=%d." % (ret_id, g_btn_ct)
        children = [output]
    else:
        output = "Button unknown! ret_id=%d; g_btn_ct=%d." % (ret_id, g_btn_ct)
        children = [output]

    g_btn_ct += 1
    return children


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

It looks like your problem was with your callback. You were returning strings vs dash components. See the code tweaks I made below. I also used no_update vs PreventUpdate (thus needing the following at the top of the file: from dash.dash import no_update)

def update_output(n_clicks1, n_clicks2, n_clicks3):
    ctx = dash.callback_context
    if not ctx.triggered[0]['value']:
        return no_update
    else:
        ret_id = ctx.triggered[0]['prop_id'].split('.')[0]

    if ret_id is None:
        # Return without updating web page.
        return no_update

    global g_btn_ct
    import time

    if ret_id == "my_button_id1":
        output = "Button %s pressed. g_btn_ct=%d." % (ret_id, g_btn_ct)
        children = [output, get_table()]
        # simulate a slow callback so we can see the loading state spinner.
        time.sleep(2)
    elif ret_id in ["my_button_id2", "my_button_id3"]:
        output = "Button %s pressed. g_btn_ct=%d." % (ret_id, g_btn_ct)
        children = html.P(output)
    else:
        output = "Button unknown! ret_id=%d; g_btn_ct=%d." % (ret_id, g_btn_ct)
        children = html.P(output)

    g_btn_ct += 1
    return children
1 Like

Thanks. That fixes this issue. My actual use case is a little more involved and I do still see issues but will look deeper now that you provided this solution.

1 Like