šŸ“£ Loading states and Loading component

Hi,

After trying again and re-writing the code it finally works for me:
The div below includes the chart and a parameter for the chart.
The other parameters are inherited from a tab component.

html.Div([
            dcc.Loading(id='loading-1',
                        children=[html.Div(dcc.Graph(id='chart_id'),
                                           className='chart_div') #custom css class
                                  ],
                        type='spinner'),
            dcc.RadioItems(
                options=[
                    {'label': 'l1', 'value': 'v1'},
                    {'label': 'l2', 'value': 'v2'},
                    {'label': 'l3', 'value': 'v3'},
                    {'label': 'l4', 'value': 'v4'},
                    {'label': 'l5', 'value': 'v5'},
                    {'label': 'l6', 'value': 'v6'},
                    {'label': 'l7', 'value': 'v7'}],
                value='v7',
                id='input3',
            ),
        ],
            className='seven columns',
            style={"padding-left": '20'}
        ),], 
           className='row', 
)

@dash_app.callback(
Output('chart_id', "figure"),
[Input('input1', 'value'),
 Input('input2', 'value'),
 Input('input3', 'value'),
 ])
 def func(input1, input2, input3):

Hope this will help you,

Cheers

2 Likes

Hi,

Really like the feature! Awesome stuff. I was wondering if you could help me with an issue Iā€™m having.

Iā€™m currently working on a multi-page app. Hereā€™s an example of the index page:

index.py

html.Div(
            [
                dcc.Location(id='url', refresh=False),
                html.Div(id="navigation_bar"),
                dcc.Dropdown(id = "options"), # users pick options which edit the query
                dcc.Loading(id="loading",children=[html.Div(id='page-content')])
            ]
        )

@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
def update_page(pathname):
     if pathname == "dashboard":
            return dashboard_layout
     else:
            display another layout

The page-content div sets a new layout every time the url changes. Each layout has its own dropdowns, graphs, tabs, etc which are populated by data from a SQL query. The query is based on the option a user picks in the options dropdown. The data from the query is then stored in a flask-cache on Redis so that all the pages can access it.

When the layout first loads, it takes a long time because the query is expensive. Subsequent steps are very quick because the queried data comes from a cache.

My issue right now is that the spinner appears for inexpensive processes (like changing a tab or clicking a dropdown) after the first data load. I would prefer if the spinner displayed only on the first time the query is run as stored in the cache.

Is there a way for me to set a ā€œspinner on first loadā€ option? Or do you see a better way for me to do this?

Please let me know.
Thanks!

2 Likes

Hi @mdylan2

you could use the decorator @app.first_callback that is alread available above your expensive first callbacks.

Cheers,

1 Like

Hi @PNhd

Iā€™m a little unsure what you mean by @app.first_callback. Is that a Dash function?

1 Like

@PNhd @mdylan2
Im facing the same issueā€¦ I want the load component only to be triggered by one input, and not whenever a change is being made on the page.

The thing is in the documentation (https://dash.plot.ly/dash-core-components/loading_component), load 1 is triggered by input one, and not both inputsā€¦ thats what id expectā€¦

The question is, is it meant to be triggered whenever a change is made or is this some sort of bug?

1 Like

@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