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.
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
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.
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.
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.