plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.34k stars 2.06k forks source link

Loading state not working correctly when layout is in a function #736

Closed valentijnnieman closed 7 months ago

valentijnnieman commented 5 years ago

When investigating https://github.com/plotly/dash-core-components/issues/488 I noticed it does not only apply to using Location components, but rather happens when the Loading components are in a layout that comes from a function. Here's the same example code as from the issue above, but with an Input component instead of a Location component:

# -*- 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
import numpy as np

from dash.dependencies import Input, Output, State

app = dash.Dash(__name__)

app.scripts.config.serve_locally = True
app.config['suppress_callback_exceptions']=True
app.layout = html.Div([
    dcc.Input(id='url'),
    dcc.Loading([html.Div(id='page-body')])
])

def make_layout(pathname):

    return html.Div([
    html.Div([
            dcc.Loading(
            id="loading-1",
            children=[html.Div(id='figure-container')],
            type="circle"),
            html.Button(id="input-1", children='Input triggers nested spinner')
        ], style={'width': 400}),
    html.Div([
            dcc.Loading(
            id="loading-2",
            children=[html.Div([dcc.Graph(id='figure-2')])],
            type="circle"),
            html.Button(id="input-2", children='Input triggers nested spinner')
        ], style={'width': 400})
    ])

@app.callback(Output('page-body', 'children'),
              [Input('url', 'value')])
def page(pathname):

    return make_layout(pathname)

@app.callback(Output("figure-container", "children"), [Input("input-1", "n_clicks")])
def input_triggers_nested(n_clicks):
    time.sleep(2)
    fig = go.Scatter(
        x=np.random.random(size=10),
        y=np.random.random(size=10)
    )

    return dcc.Graph(figure=dict(data=[fig]), id='figure-1')

@app.callback(Output("figure-2", "figure"), [Input("input-2", "n_clicks")])
def input_triggers_nested(n_clicks):
    time.sleep(2)
    fig = go.Scatter(
        x=np.random.random(size=10),
        y=np.random.random(size=10)
    )
    return dict(data=[fig])

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

Because the Loading components are being appended to page-body via the make_layout function, the spinners aren't showing upon initial loading. I suspect this is because we're not setting the loading_state property correctly on nested components, or, the requestQueue does not handle nested component updates properly.

cannatag commented 4 years ago

I have the same problem on a multipage app. Any news on this?

bennyweise commented 4 years ago

I've also just discovered this problem on my multipage app, so would be interested in any updates as well.

CP-Vub commented 4 years ago

I as well am having similar erratic loading animation behavior with my multipage app, so just like the people above me, I would be interested in an update regarding this issue.

locha0519 commented 4 years ago

I had the same problem following the tutorial but I found two workarounds to solve it.

One is using dcc.Interval to refresh the page after loading it, and limiting the number of updates by max_intervals. However, this method is inefficient since it will unnecessarily refresh the page several times.

The better solution I found is to combine dash apps with WSGI Apps. I modified the tutorial and tested the method on my app, witch is formed by an image gallery (index page) and zoom-in images (zoom-in pages). The method works well and is faster than the previous workaround. Here is a simplified test code, you can have a try:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from flask import Flask, redirect
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

# Host wsgi app
flask_app = Flask(__name__)

@flask_app.route('/')
def index():
    # redirect to image gallery dash app
    return redirect('/index')

# Image gallery dash app
app_index = dash.Dash(
    'app_index',
    requests_pathname_prefix='/index/'
)
app_index.layout = html.Div([
    html.H1('Image gallery'),
    html.Div([
        html.A(
            html.Img(src='http://www.kanahei.com/upload/2019/12/12_wpsam.jpg'),
            href='/zoomin/image1'),
        html.A(
            html.Img(src='http://www.kanahei.com/upload/2019/11/11_wpsam.jpg'),
            href='/zoomin/image2'),
    ])
])

# Zoomin images dash app
app_zoomin = dash.Dash(
    'app_zoomin',
    requests_pathname_prefix='/zoomin/'
)
app_zoomin.layout = html.Div([
    dcc.Location(id='url'),
    html.H1('Zoomin image'),
    html.Div([
        html.Div(id='pathname'),
        html.Img(id='zoomin_image',
                 style={'max-width': '600px',
                        'max-height': '600px'}),
    ]),
    html.Div([
        html.A('Go back to gallery', href='/'),
    ])
])

@app_zoomin.callback(
    [Output('zoomin_image', 'src'),
     Output('pathname', 'children')],
    [Input('url', 'pathname')])
def update_zoomin_images(pathname):
    if pathname == '/zoomin/image1':
        return 'http://www.kanahei.com/upload/2019/12/01_16x9.jpg', pathname
    elif pathname == '/zoomin/image2':
        return 'http://www.kanahei.com/upload/2019/11/01_16x9.jpg', pathname
    return None, pathname

# Combine apps
application = DispatcherMiddleware(flask_app, {
    '/index': app_index.server,
    '/zoomin': app_zoomin.server,
})

# Run server
if __name__ == '__main__':
    run_simple('0.0.0.0', 8050, application)
MM-Lehmann commented 4 years ago

I can confirm this issue. Is there any plan in place to get this resolved?

orangeluc commented 4 years ago

Have the same issue, is there any update on this? Thx!

mschrader15 commented 4 years ago

I have the same issue as well

aoelvp94 commented 4 years ago

Same here. Any update?

JamesMckenzie4 commented 3 years ago

Was there any solution to this problem? Cheers.

chaseedge commented 2 years ago

Just keeping this alive, I'm having the same problem.

tuchandra commented 1 year ago

I'm running into this too. I confirmed with vanilla dash, but first encountered this when using the excellent dash-mantine-components. The library demos a button that shows a loading spinner & greys itself out while being clicked (link), but it unfortunately does not work if the button is defined in a function.

Defining components within functions is absolutely essential for anything more than the simplest of apps. The code gets too out of hand with everything defined at the module level; it's disappointing that this doesn't work. I don't know where to begin debugging this or if help is even wanted, but I'd be willing to try digging in myself with a little guidance.

idling-mind commented 10 months ago

Having the same issue. Is there any workaround for this?

AnnMarieW commented 8 months ago

I know this is an old issue, but there seems to be recent activity. I ran the original example and everything looks fine to me. Can anyone provide a recent example that replicates this issue? Just asking because I'm working on a dcc.Loading PR right now.

dcc_loading_736

Coding-with-Adam commented 7 months ago

closed by #2760