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

[BUG] JavaScript callbacks don't trigger Python callbacks #1774

Closed
slochower opened this issue Sep 23, 2021 · 4 comments
Closed

[BUG] JavaScript callbacks don't trigger Python callbacks #1774

slochower opened this issue Sep 23, 2021 · 4 comments

Comments

@slochower
Copy link

Python callbacks don't appear to respond to elements (at least html.divs) modified by a JavaScript callback.

Describe your context

I'm using macOS 10.15.7 with Python 3.8.6.

dash                             1.18.1
dash-bootstrap-components        0.11.1
dash-core-components             1.14.1
dash-dangerously-set-inner-html  0.0.2
dash-daq                         0.5.0
dash-extensions                  0.0.53
dash-html-components             1.1.1
dash-renderer                    1.8.3
dash-table                       4.11.1

This issue is independent of browser.

Describe the bug

Python callbacks don't appear to respond to elements (at least html.divs) set by a JavaScript callback.

I'm trying to trigger Python server-side callbacks after a JavaScript client-side callback. In this case, I'm going through divs. (If there is a way to have the JS directly interact with a dcc.Store component, that'd be great.) I can have JS set the content of a div but then Python callbacks that use that div as an Input never trigger.

The basic setup is like this:

app.layout = html.Div(
    children=[
        dbc.Button(
        "Button",
        id="button",
        n_clicks=0,
        ),
        html.Div(id="div1", children="initial string div1"),
        html.Div(id="div2", children="initial string div2"),
        html.Div(id="div3", children="initial string div3"),

    ]
)

app.clientside_callback(
    """
    // Do some complicated calculation...
    function() {
        $("#div2").text("text");
    }
    """,
    Output("div1", "children"),
    Input("button", "n_clicks"),
    prevent_initial_call=True,
)

@app.callback(
    [Output("div3", "children")],
    [Input("div2", "children")],
    prevent_initial_call=True
)
def sketcher(children):
    return [children]

div2 ends up being set by JS as expected, but the second callback (sketcher), never gets triggered and div3 keeps its initial value. It seems (to me) that Dash is unaware that the JS callback modified div2.

I am not tied to this approach–going through divs. I just need a way to have a JS callback trigger subsequent Python callbacks. Is this possible?

To be clear, if I use a regular Python callback to set div2 instead of JS, then div3 also gets set.

@app.callback(
    [Output("div2", "children")],
    [Input("button", "n_clicks")],
    prevent_initial_call=True
)
def set2(n):
    return ["div2 is set"]

Expected behavior

I would expect the Python callback to be triggered by the JS callback.

@alexcjohnson
Copy link
Collaborator

That’s correct, if you use JavaScript to set the content of the DOM directly, dash is unaware of what you did. Instead, make div2 another output of that clientside callback and include it’s text in the callback return value.

@slochower
Copy link
Author

Hmm, I see, thanks for the clarification. My motivation for framing it this way is because the JS code that I have to work with is using Promises. I know I can't return the Promise directly to Dash, so I thought I might be able to change something on the page that Dash could pick up.

Is there any way to trigger Python callbacks after running JS using Promises? I'm just looking for a way to get back into the "regular" Pythonic flow of callbacks after running a bit of JS.

@alexcjohnson
Copy link
Collaborator

Ah, got it. Promise handling is a known limitation of clientside callbacks - see #1364, it hasn't gotten to the top of the list for anyone here but it shouldn't be hard, if you want to take a stab at it we'll certainly help you get it across the finish line.

I wouldn't want to look for a hack that avoids baking this into the renderer, that would have all sorts of weird consequences, maybe most importantly for you that Dash doesn't use the DOM as its source for callback inputs, it keeps a separate layout tree of simple JS objects.

@slochower
Copy link
Author

Yep, I see that. I'd love to help, but I'm afraid my knowledge of TypeScript is close to nil.

In this case, I was able to work around the problem by finding a way out of the Promise and returning a value from the JavaScript callback.

I'm going to close this issue because it is documented that Dash does not support Promises; here I was trying to work around the problem by directly setting the contents of a div inside the Promise and trying to use that to trigger other Dash callbacks.

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

No branches or pull requests

2 participants