šŸ“£ 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!

4 Likes

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!

1 Like

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.

2 Likes

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')]

3 Likes

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)
1 Like

: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.

@tcoleman did you find a solution to this yet? Did anybody else?

I would like to display a full page loading spinner only on first page load, but not on subsequent callbacks.

(for one it tends to scroll back to the top of the screen when triggered, which is not the desired behaviour)

2 Likes

@oegedijk Iā€™m afraid not. In the end I achieved an acceptable result by splitting up my callbacks, optimising the ones that need rapid refresh to make them load quickly, and putting the others on a much slower refresh.

But I agree, having an ā€˜only on initial loadā€™ option for the spinner would be an excellent feature.

3 Likes

But I agree, having an ā€˜only on initial loadā€™ option for the spinner would be an excellent feature.

@chriddyp any plans for such a feature? In my case I have quite a big dashboard with many tabs and plots that all load in the beginning. (see titanicexplainer.herokuapp.com) This means that de dashboard is sort-of but not really useable while it is still loading. I would like to wrap the whole layout in a spinner on initial load, but not after.

(if I wrap the whole layout in a spinner, then it spins everytime something changes and then resets the scroll to the top of the page)

1 Like

Hi,

Do anyone know if its possible to achieve that a Loading component checks the loading_state property only on its first children?

I show a spinner during the loading process of a collapse section. The content is quite complex, it includes tables, graphs, etc. My problem is that whenever a callback is triggered from one of the components inside the collapse section, the whole section disappears for a while and the spinner is shown. I would like to see the spinner only when the collapse is opened, i.e., only when the first children is in loading_state = True.

Helped me a lot, Thanks!

Hi @chriddyp! Is it possible to apply this in a straightforward way to the actual Loading component? I mean, to make it appear if the loading takes more than a second or so, and not for any slight change?

The thing is that I have the same issue than @Mike3 and a lot of times the component only take a few miliseconds to appear but it starts the Loading animation anyway. And because the callback itā€™s connected to component that change pretty often, is a very annoying thing.

Thanks in advance!

1 Like

Wow itā€™s June 2021 and still no solution to this it seems :sweat_smile: Iā€™m stuck with the same issue and was wondering: did anyone find a solution to this?

1 Like