Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dcc.Loading is not accessible by DOM in Dash 2.18.2 #3097

Open
Kissabi opened this issue Nov 29, 2024 · 2 comments
Open

dcc.Loading is not accessible by DOM in Dash 2.18.2 #3097

Kissabi opened this issue Nov 29, 2024 · 2 comments
Labels
feature something new P3 backlog

Comments

@Kissabi
Copy link

Kissabi commented Nov 29, 2024

Description

The dcc.Loading component's children and elements within custom_spinner are not accessible via document.getElementById in clientside callbacks when using Dash 2.18.2. This prevents direct DOM manipulation within these elements.

Steps to Reproduce

The following minimal example demonstrates the issue:

from dash import Dash, html, Input, Output, dcc

app = Dash(prevent_initial_callbacks=True)

app.layout = html.Div([
    html.Button("Button 1", id="btn1"),
    html.Button("Button 2", id="btn2"),
    html.Button("Button 3", id="btn3"),
    dcc.Loading(html.P(id="logA"), overlay_style={"visibility":"visible", "opacity": 0.5, "backgroundColor": "#000"}, custom_spinner=html.Div(id="logB"), id="logC")
])

# Works (accessing direct child of layout)
app.clientside_callback(
    """
    async function(){
        const content = document.getElementById("logA");

        if (!content) {
            console.error("content element not found!");
            return;
        }
		
        await new Promise(r => setTimeout(r, 10000)); 
        return "Test";
    }
    """,
    Output("logA", "children"),
    Input("btn1", "n_clicks"),
    prevent_initial_call=True
)

# Does not work (accessing custom_spinner element)
app.clientside_callback(
    """
    function(){
        const content = document.getElementById("logB");

        if (!content) {
            console.error("content element not found!");
            return;
        }
        
        return "loading test...";
    }
    """,
    Output("logB", "children"),
    Input("btn1", "n_clicks"),
    prevent_initial_call=True
)

# Does not work (accessing the Loading component itself)
app.clientside_callback(
    """
    function(){
        const content = document.getElementById("logC");

        if (!content) {
            console.error("content element not found!");
            return;
        }
        
        return "loading test...";
    }
    """,
    Output("logC", "children"),
    Input("btn1", "n_clicks"),
    prevent_initial_call=True
)

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

Expected Behavior

All elements with assigned IDs (logA, logB, and logC) should be accessible via document.getElementById within clientside callbacks.

Actual Behavior

Only logA (the direct child of the layout, outside custom_spinner and not the dcc.Loading component itself) is accessible. logB (inside custom_spinner) and logC (the dcc.Loading component) are not.

@gvwilson gvwilson added bug something broken P3 backlog labels Dec 3, 2024
@AnnMarieW
Copy link
Collaborator

AnnMarieW commented Dec 5, 2024

I don't think this is a bug. The loading spinner is available in the dom only when the component is loading.

If you change the second callback to add a delay, to allow some time for the spinner to render it works fine. The third callback should be deleted because it replaces the component (logA) that is being updated and makes no sense to do that.

from dash import Dash, html, Input, Output, dcc

app = Dash(prevent_initial_callbacks=True)

app.layout = html.Div([
    html.Button("Button 1", id="btn1"),
    html.Button("Button 2", id="btn2"),
    html.Button("Button 3", id="btn3"),
    dcc.Loading(html.P(id="logA"), overlay_style={"visibility": "visible", "opacity": 0.5, "backgroundColor": "#000"},
                custom_spinner=html.Div(id="logB"), id="logC")
])

# Works (accessing direct child of layout)
app.clientside_callback(
    """
    async function(){
        const content = document.getElementById("logA");

        if (!content) {
            console.error("content element not found!");
            return;
        }

        await new Promise(r => setTimeout(r, 10000)); 
        return "Test";
    }
    """,
    Output("logA", "children"),
    Input("btn1", "n_clicks"),
    prevent_initial_call=True
)

app.clientside_callback(
    """
    function(n_clicks) {
        if (!n_clicks) {
            return null;
        }

        // Poll until the element is available
        return new Promise((resolve) => {
            const interval = setInterval(() => {
                const content = document.getElementById("logB");
                if (content) {
                    clearInterval(interval);
                    resolve("loading test...");
                }
            }, 100); // Check every 100ms
        });
    }
    """,
    Output("logB", "children"),
    Input("btn1", "n_clicks"),
    prevent_initial_call=True
)

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

@gvwilson gvwilson added feature something new and removed bug something broken labels Dec 6, 2024
@yuvashrikarunakaran
Copy link
Contributor

import dash
from dash import html, dcc, Input, Output, ctx

app = dash.Dash(name)

app.layout = html.Div([
dcc.Loading(
id="loading-container",
type="default",
children=[
html.Div("Content to manipulate", id="content")
],
custom_spinner=html.Div(id="custom-spinner", children="Loading...")
),
html.Button("Trigger Loading", id="load-btn")
])

@app.callback(
Output("content", "children"),
Input("load-btn", "n_clicks"),
prevent_initial_call=True
)
def trigger_loading(n_clicks):
return f"Updated content {n_clicks}"

if name == "main":
app.run_server(debug=True)

client-side:

setInterval(() => {
const content = document.getElementById("content");
const spinner = document.getElementById("custom-spinner");

if (content) {
    console.log("Content element found:", content.innerHTML);
} else {
    console.log("Content element not found");
}

if (spinner) {
    console.log("Spinner element found:", spinner.innerHTML);
} else {
    console.log("Spinner element not found");
}

}, 1000);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature something new P3 backlog
Projects
None yet
Development

No branches or pull requests

4 participants