Changing the value of a input from javascript callback does not trigger the callback associated with that input

my goal is to trigger a callback from the keyboard key press. but I found after some googling that dash has limited capability of doing that. I need to use JS and clientside_callback to capture keyboard events. am i correct or dash can do this natively?

so i wrote a clint-side callback which will change the value of an input field upon pressing some desired key. this is working fine. the value is changing.

The second callback i wrote, which is supposed to trigger if the input field is changed. but when JS changes the value of the input, the callback is not triggering but if I manually type inside the input field, then the callback is triggering fine for each keypress. Here is my code:

import dash
from dash import html, dcc
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
app = dash.Dash(__name__)
app.layout = html.Div([
    html.Div(id="output"),
    html.Button("Invisible Button", id="invisible-button", style={"opacity": "0", "position": "absolute", "left": "-9999px"}),
    dcc.Input(id='last-key-pressed'),

])


app.clientside_callback(
    """
    function(n_clicks) {
        document.addEventListener('keydown', function(event) {
            const allowedKeys = ['ArrowLeft', 'ArrowRight', 'Enter'];
            if (allowedKeys.includes(event.key)) {
                return event.key;  // Return an object with the pressed key
                document.getElementById('last-key-pressed').dispatchEvent(new Event('input'));
            }
        });
        return window.dash_clientside.no_update;  // Return no_update if not an allowed key
    }
    """,
    Output('last-key-pressed', 'value'),
    [Input('invisible-button', 'n_clicks')],
)


@app.callback(
    Output('output', 'children'),
    [Input('last-key-pressed', 'value')],
    prevent_initial_call=True
)
def handle_key_press(last_key_data):
    print(last_key_data)
    if last_key_data is None:
        raise PreventUpdate
    last_key_pressed = last_key_data

    return f"Last key pressed: {last_key_pressed}"

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

Hi @mainul

Good timing! This is possible as of dash 2.16.1 by using dash_clientside_set_props
The docs aren’t available yet, but you use it like this:

dash_clientside.set_props(id, {props to set})
           

so in your case it would be:

dash_clientside.set_props("last-key-pressed", {value: event.key})

Here’s the whole callback


app.clientside_callback(
    """
    function(n_clicks) {
        document.addEventListener('keydown', function(event) {
            const allowedKeys = ['ArrowLeft', 'ArrowRight', 'Enter'];
            if (allowedKeys.includes(event.key)) {
                dash_clientside.set_props("last-key-pressed", {value: event.key})
            }
        });
        return window.dash_clientside.no_update;  // Return no_update if not an allowed key
    }
    """,
    Output('last-key-pressed', 'value'),
    [Input('invisible-button', 'n_clicks')],
)


4 Likes

Great. this works. Does this also works for dcc.Store. So, instead of changing a value in the input field, i want to change the value in dcc.Store from clientside_callback and that chhange should trigger a dash callback

yes, i checked. it works with the following line:
dash_clientside.set_props("last-key-pressed", {data: event.key})

This should work for anything that is controlled by Dash, the key being that it has to be defined with an id. This will also work for anything that doesnt currently exist in the DOM. (Modals, popovers, etc)

Is there any way to get_props?

In developer’s consol I can find only set_props for props search.
image

Hello @kuplo,

Not currently, I did think about it though.

Why are you looking for it?

To be able to read properties from raw javascript to use them (much quicker)

set_props is a new addition, get_props might come out in a later release.

1 Like

For example, now I create tasks by adding children to output_result in callback

@app.callback(
    [
        Output("output_result", "children"),
        Output("interval", "disabled"),
    ],
    [Input("button", "n_clicks")],
    [
        State("text", "value"),
        State("output_result", "children"),
    ],
)
def submit_to_multiple_tasks(n_clicks, text, output_result):

But such tasks can come from external source. I want to display them in the same output_result.
With get_props I would run some javascript likt

more_tasks = [task1, task2,...]
children = get_props('output_result', 'children')
children = [...children, ...more_tasks]
set_props('output_results', {children:children})

Right now I don’t know how to do that in the same output_results. So I should create ‘output_results_external’ by raw javascript, and later add children by raw javascript.