Hi,
I try to use a spinner for some graphs which contain lots of data retrieved from an sql database, hence they need some time to load. Using the ._dash_loading-callback css style I can manage to use a simple css spinner (see code below). The usage of the new [data-dash-is-loading=“true”] element seems not to do what I want, as it controls the styles while the input is active (e.g. see this post loading-states-api-and-a-loading-component-prerelease where you also find the code for the dash app, slightly altered)
I am so fare satisfied with the possibility to have a spinner working, but the spinner is positioned at the bottom of the page and not where I would expect it. Actually I would appreciate if the spinner would replace the element which is actually updated. Using the example code below, you can observer the following behavior:
- On page load, the spinner is below the tabs where it is expected to be
- when switching tabs the spinner is shown below the graph (and does not replace the graph)
My question is, if there is a method to make the spinner replace the to be updated element(s)?
Example code: dash_server_with_spinner.py
# -*- coding: utf-8 -*-
import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.graph_objs as go
import time
from dash.dependencies import Input, Output, State
app = dash.Dash(__name__)
app.scripts.config.serve_locally = True
app.title = "Testing css spinner"
app.layout = html.Div(
children=[
html.Div(
children = [
html.H4('Example loading tabs with own css (class) style'),
]
),
dcc.Tabs(
id="tabs",
value="tab-1",
children=[
dcc.Tab(label="Bar Graph", value="tab-1"),
dcc.Tab(label="Scatter Graph", value="tab-2"),
]),
html.Div(id='tabs-content'),
],
)
@app.callback(Output('tabs-content', 'children'),
[Input('tabs', 'value')])
def render_content(tab):
time.sleep(2) # allow time to see spinner
if tab == 'tab-1':
_layout = html.Div(id='loading-1', children=[
dcc.Graph(
id='graph-2-tabs',
figure=go.Figure(
data=[
go.Bar(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[219, 146, 112, 127, 124, 180, 236, 207, 236, 263,
350, 430, 474, 526, 488, 537, 500, 439],
name='Rest of world',
marker=go.bar.Marker(
color='rgb(55, 83, 109)'
)
),
go.Bar(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
299, 340, 403, 549, 499],
name='China',
marker=go.bar.Marker(
color='rgb(26, 118, 255)'
)
)
],
layout=go.Layout(
title='US Export of Plastic Scrap',
showlegend=True,
legend=go.layout.Legend(
x=0,
y=1.0
),
margin=go.layout.Margin(l=40, r=0, t=40, b=30)
)
),
)
])
elif tab == 'tab-2':
_layout = html.Div(id='loading-2', children=[
dcc.Graph(
id='graph-1-tabs',
figure=go.Figure(
data=[
go.Scatter(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[219, 146, 112, 127, 124, 180, 236, 207, 236, 263,
350, 430, 474, 526, 488, 537, 500, 439],
name='Rest of world',
marker=go.Marker(
color='hotpink'
)
),
go.Scatter(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
299, 340, 403, 549, 499],
name='China',
marker=go.Marker(
color='gold'
)
)
],
layout=go.Layout(
title='US Export of Plastic Scrap',
showlegend=True,
legend=go.layout.Legend(
x=0,
y=1.0
),
margin=go.layout.Margin(l=40, r=0, t=40, b=30)
)
),
)
])
print(_layout)
return _layout
if __name__ == "__main__":
app.run_server(debug=False)
The css file (to be places in the assets folder) is typography.css:
/* simple circular css spinner
*
* why does this one needs to be placed in ._dash-loading-callback
* and not in ._dash-loading-callback:after as the other spinners?
*
* Change size with setting fontsize, and position with margin
*/
._dash-loading-callback {
margin: 60px auto;
width: 7em;
height: 7em;
font-size: 7px;
position: relative;
border-radius: 50%;
border-top: 1.1em solid rgba(0, 0, 0, 0.2);
border-right: 1.1em solid rgba(0, 0, 0, 0.4);
border-bottom: 1.1em solid rgba(0, 0, 0, 0.6);
border-left: 1.1em solid #abc432;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation: load_spinner 0.95s infinite linear;
animation: load_spinner 0.95s infinite linear;
}
@-webkit-keyframes load_spinner {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes load_spinner {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}