✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🧬 Learn how to build RNA-Seq data apps with Python & Dash. Register for the May 20 Webinar!

Dash plugin execute javascript

Hey all,

I have maybe a naive question. I have created a custom Dash plugin which loads a javascript library when the app starts. Now I want to run a javascript function (defined in the library) whenever a button is clicked.

So I have test.react.js which is roughly

 render() {
        const {id, label, setProps, value} = this.props;

        return (
            <div id={id}>
                <input
                    type={"hidden"}
                    value={value}
                    onChange={e => {
                        doSomething(e.target.value);
                        }
                    }}
                />
            </div>
        );

and a usage.py which looks like

app.layout = html.Div([
    html.Button(id='button', children="hello"),
    custom.custom(
        id='input',
        value='my-value',
    )
])


@app.callback(Output('input', 'value'), [Input('button', 'n_clicks')])
def display_output(value):
    return 'You have entered {}'.format(value)

It appears in the DOM that the value is updated correctly to ‘You have entered 1’ when I click the button but the callback in custom doSomething(e.target.value); never gets called.

What am I doing wrong here? Aside from not reading a book on React :wink:

You need to use setProps to send callback back to the server.

Hey Philippe,

I am not trying to send callback to the server rather have the server tell the site to run a javascript function.

You’d still to use setPropsfor that in the onChange handler. The generated component in the boilerplate is an input wrapper you can use as example:

Hey Philippe,

I omitted some of the code. I am using exactly that template. The onChange callback gets called when I change the from the browser and setProps is set and called. When I change the input from python using a button to generate a callback which changes the s value the onChange callback does not get called at all.

The full code snippet is below. The else branch of the if statement is never called.

                <input
                    value={value}
                    onChange={e => {
                        if (setProps) {
                            setProps({
                                value: e.target.value
                            });
                        } else {
                            doSomething(e.target.value);
                            this.setState({
                                value: e.target.value
                            })
                        }
                    }}
                />

So you only call doSomething inside the else, it will only be called if the component is not connected to a callback. You can move doSomething out of the if/else if you want it to be called every time.

Hey Philippe,

Thanks for the time. I don’t think I am explaining myself well. I have copied my full python script and full react component below to try and be more clear. Basically I want to trigger a callback in python via a web action (click a button, change a dropdown, update a form etc). This callback in python will a) update the dom (new graph, table or other standard dash/plotly component) and b) tell my custom component to execute a client side javascript function call.

I am using the stock custom component plugin and have only added a button to trigger a callback and a function to write a log line. However if I change the via the web the onChange gets called (as well as the original python callback) however if I click the button the value of input changes but the onChange callback doesn’t get called.

Maybe this isn’t the ideal way to make my function get called but I am at a loss for how to do it.

Hopefully that is more clear.

usage.py

import testcomp
import dash
from dash.dependencies import Input, Output
import dash_html_components as html

app = dash.Dash(__name__)

app.scripts.config.serve_locally = True
app.css.config.serve_locally = True

app.layout = html.Div([
    html.Button(id='button', children="hello"),
    testcomp.testcomp(
        id='input',
        value='my-value'
    ),
    html.Div(id='output')
])


#new callback. This updates value but does not trigger onChange
@app.callback(Output('input', 'value'), [Input('button', 'n_clicks')])
def display_output(value):
    return 'You have entered {}'.format(value)


#original callback (This works and triggers onChange)
@app.callback(Output('output', 'children'), [Input('input', 'value')])
def display_output(value):
    return 'You have entered {}'.format(value)

if __name__ == '__main__':
    app.run_server(debug=True, host="0.0.0.0")

testcomp.react.js

import React, {Component} from 'react';
import PropTypes from 'prop-types';

export default class testcomp extends Component {

    render() {
        const {id, label, setProps, value} = this.props;

        return (
            <div id={id}>

                <input
                    type={"text"}
                    value={value}
                    onChange={e => {
                        eventChange(e.target.value); //this is only triggered when I change the input from the browser. Never when I change the value from python (new callback)
                        if (setProps) {
                            //this is triggered when i change the input from the browser
                            setProps({ 
                                value: e.target.value
                            });
                        } else {
                            //this is never triggered
                            this.setState({
                                value: e.target.value
                            })
                        }
                    }}
                />
            </div>
        );
    }
}

function eventChange(value) {
    console.log("value", value);
}


testcomp.defaultProps = {};

testcomp.propTypes = {
    /**
     * The ID used to identify this component in Dash callbacks
     */
    id: PropTypes.string,

    /**
     * A label that will be printed when this component is rendered.
     */
    label: PropTypes.string.isRequired,

    /**
     * The value displayed in the input
     */
    value: PropTypes.string,

    /**
     * Dash-assigned callback that should be called whenever any of the
     * properties change
     */
    setProps: PropTypes.func
};

For the reference of people like me who came to this thread while googling how to fire Javascript when returning a callback from the server, here’s a solution that works using the Client-side Callback mechanism built into Dash.

In the assets folder of your app create a javascript file which holds the function you want to trigger:

if (!window.dash_clientside) {
    window.dash_clientside = {};
}

const baseHref = "/home/dataset/";

// create the "ui" namespace within dash_clientside
window.dash_clientside.ui = {
    // this function can be called by the python library
    jsFunction: function(arg1) {
        console.log(arg1)
    }
}

And in your python file, configure the callback like this to fire when the call is sent to the server:

    app.clientside_callback(
        # specifiy the callback with ClientsideFunction(<namespace>, <function name>)
        ClientsideFunction('ui', 'jsFunction'),
        # the Output, Input and State are passed in as with a regular callback
        Output('output', 'children'),
        [Input('input', 'value')]
    )

by giving the clientside callback the same input triggers, you can have it fire when the server-side callback is triggered, or if you set its input as the output of the server-side callback, then it will fire once that callback returns a value.

1 Like