📣 Loading states and Loading component

@mdylan2 so my work around was:
instead of having the my output div as a child for the load button, I put it independently in the app.layout, and in the callback, I had multiple outputs: namely the load button and the intended output div.

Hope that helps!

Oh I see, so you define the loading component div and output div separately and update them simultaneously in a multi-output callback.

That’s a smart workaround! Thanks!

I am having the same question. Anybody has any answers for it? Thanks.

Is there any way currently to get the Loading animation to display only after a few seconds? I have a callback that sometimes takes 3+ seconds to load, so this new component could be quite useful in those cases, but most of the time the callback only takes an invisible millisecond to load.

With dcc.Loading now, those millisecond updates now become visible as dcc.Loading seems to load its animation at any slightest delay which it detects.

1 Like

Finally, I placed

dcc.Loading(loading_state={'is_loading':True})]

so it ‘is loading’ permanently. I toggle the visibility of the whole div in a callback.

I hope it helps.

1 Like

Is it possible to get the actual State of the component to see if its loading as part of a callback?

Something along the lines of:
State('input-1', 'data-dash-is-loading')]

I’m still working on the details and don’t have a fully working example yet, but with the CSS approach it looks like CSS transitions will help. Something like this:

#root {
    visibility: visible;
    transition-property: visibility;
    transition-delay: 0s;
}

#root[data-dash-is-loading="true"] {
    visibility: hidden;
    transition-delay: 5s;
}

will hide element #root if it’s been loading for more than 5 seconds.

Maybe someone who knows more about JS and CSS than me (which is any web developer) would be able to turn that into a full example.

2 Likes

Is there a way to make to Loading component only active on the initial page load?

I’m working on a dashboard that gets updated once per minute by an Interval component, so I don’t want the loading spinner to appear every minute when the charts are refreshed. However it would be nice to get a spinner when the page is first loaded.

@Mike3, Did you ever find a solution?

I did not, though I did not look into the issue too deeply, either. Since I do not know web development, I’ll just be hoping a developer or a contributor adds a delay functionality ^^; , especially since this is by far the prettiest and “hi-tech” feature I’ve seen.

Here’s an example of delay

.delay {
	position: relative;
}

.delay::after {
    content: "";
    display: block;
	position: absolute;
	top: 0;
	left: 0;
	/* min and max vals in case children (content)
	 * does not exist at all until the callback
     * processes */
	min-height: 18px;
	min-width: 181px;
	width: 100%;
	height: 100%;
	background-color: rgba(255, 255, 255, 0.4);
	animation: fadeout 2s forwards;
}

.delay[data-dash-is-loading="true"]::after{
	content: "";
	display: block;
	background-color: rgba(255, 255, 255, 0.4);
	/* 2s delay + 0.5s transition */
	animation: fadein 2.5s forwards;
}

@keyframes fadein {
    0% { opacity: 0; }
    /*
     * as above, the the entire transition is 2.5 seconds long.
     * so, to "delay" the loading screen for 2 seconds,
     * keep it "hidden" (opacty: 0) for 80% of the
     * total of transition time: 0.8 * 2.5 = 2 seconds
     */
    80% { opacity: 0; }
    100% { opacity: 1; }
}

@keyframes fadeout {
    from { opacity: 1;}
    to { opacity: 0;}
}
import dash
from dash.dependencies import Input, Output, State
import dash_html_components as html
import dash_core_components as dcc
import plotly.graph_objs as go
import time

app = dash.Dash(__name__)

style = {'width': 300, 'marginLeft': 'auto', 'marginRight': 'auto'}

app.layout = html.Div(
    children=[
        html.Div(style=style, children=[
            dcc.Input(id="input-1", value="new value"),
            html.Div("old value", className='delay', id="output-1")
        ]),

        html.Div(style=style, children=[
            dcc.Input(id="input-2", value="input causes loading"),
            html.Div(dcc.Graph(
                figure={
                    'data': [
                        go.Scatter(
                            x=[1, 2, 3, 4, 5],
                            y=[1, 5, 1, 3, 4],
                        )
                    ]
                }
            ), className="delay", id="output-2")]),
    ]
)


@app.callback(
    Output("output-1", "children"),
    [Input("input-1", "value")])
def input_triggers_spinner(value):
    time.sleep(5)
    return value


@app.callback(
    Output("output-2", "children"),
    [Input("input-2", "value")])
def input_triggers_nested(value):
    time.sleep(5)
    return dcc.Graph(figure={
        'data': [
            go.Scatter(
                x=[1, 2, 3, 4, 5],
                y=[1, 5, 1, 3, 4],
                marker = dict(
                    size = 10,
                    color = 'rgba(152, 0, 0, .8)',
                    line = dict(
                        width = 2,
                        color = 'rgb(0, 0, 0)'
                    )
                )
            )
        ],
        'layout': {'title': value}
    })

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

:open_mouth: To use the top css code, I am supposed to save it as a css file, and put it in the app’s folder under another folder “Assets”, am I right? And then simply utilizing className='delay' under your Div of choice replicates a delayed loading animation?

Doing the above on the Dash code you provided, I see how that it works, but this technique does not seem to replicate for my own Dash app, but oh well. Now we may have something to work with.

There is an error: “l.gridlayer is undefined ( This error originated from the built-in JavaScript code that runs Dash apps.)” when the following graph is wrapped by Loading component:

layout = go.Layout(
        xaxis=dict(
            autorange=True,
            showgrid=False,
            zeroline=False,
            showline=False,
            ticks='',
            showticklabels=False
        ),
        yaxis=dict(
            autorange=True,
            showgrid=False,
            zeroline=False,
            showline=False,
            ticks='',
            showticklabels=False
        )
    )
go.Figure(data=[], layout=layout)

If I place {} instead everything works fine, but looks ugly - empty grid and axes until first figure is plotted.

three reasons why you should buy plotly pro: support open source, get great support, host your plots and dashboards online