I used a hack to “solve” the problem. The approach is as follows:
- Create an invisible react Input field (known to dash)
- Use the normal @app.callback, referencing the ID of the invisible field
- Add javascript function to dispatch synthetic react event using the Input field
- From within html code can call the javascript function with a payload
Here is the javascript (which should be put in assets dir):
var __rendezvous_count = 0;
var __rendezvouz_setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
/**
* Send synthetic react event to application (from client)
* - detailed here: https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js
*
* @param payload payload as string
*/
function sendEventToApp(payload) {
var rendezvous = document.getElementById("ExternalEvent");
var newvalue = payload + ":" + __rendezvous_count++;
__rendezvouz_setter.call(rendezvous, newvalue);
var ev = new Event('input', { bubbles: true });
rendezvous.dispatchEvent(ev);
}
Here is a simple contrived test:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_dangerously_set_inner_html
app = dash.Dash(__name__, external_stylesheets = [dbc.themes.UNITED])
def ExternalEvents():
return dbc.Input(type='text', id='ExternalEvent', placeholder='', style={'visibility': 'hidden'})
def HTML(html):
return dash_dangerously_set_inner_html.DangerouslySetInnerHTML(html)
@app.callback(Output('target', 'children'), [Input('ExternalEvent', 'value')])
def update_input(value):
return "Click Me" if value is None else value
app.layout = dbc.Container([
html.H2("Click Me", id='target'),
html.Div([
HTML("""<button onclick='sendEventToApp("button1")' class="btn">test</button>""")
]),
ExternalEvents(),
])
app.run_server()