[Dash Extensions] EventListener trigger for only the "Enter" key

I’m working with a Dash Mantine TextInput component. I want to trigger a callback only when the “Enter” key is pressed. I’ve gotten a working prototype using a Dash Extensions EventListener. However, it seems that an EventListener can only trigger a “keydown” event for the keys listed in the documentation – “key”, “ctrlKey”, etc. I really don’t like how the server code needs to check for the “Enter” key on every key press and ignore everything but the “Enter” key:

if "key" in event:
    if event["key"] != "Enter":
        return no_update

At this point I’m leaning towards creating my own clientside_callback to simulate a button click when the “Enter” key is pressed. Before I do, can someone tell if I’m doing something wrong with EventListener? Can I specify a key in the “events” prop like the following:

de.EventListener(
    dmc.TextInput(
        id="message",
    ),
    events=[{"event": "keydown", "props": ["Enter"]}],
    id="enter-key-listener",
),

Thanks in advance for any response!

You can use the Keyboard component instead. I haven’t had time to push the docs, but you can probably figure out how to use it from the property definitions,

2 Likes

I used the Keyboard component, and it triggered great when I hit the “Enter” key. However, it triggered twice. Here’s my code:

de.Keyboard(
    dmc.TextInput(
        id="message",
    ),
    captureKeys=["Enter"],
    id="enter-key-listener",
),

Here’s the callback code:

@callback(
    Output("message", "value"),
    Input("enter-key-listener", "n_keydowns"),
    State("message", "value"),
    prevent_initial_call=True,
)
def process_message(_, message):
    print("message: ", message)
    return no_update

The message will print twice. At one point, I printed out “ctx.triggered” and it would display “n_keydowns” as 1 and 2, 3 and 4, etc.

Are you running in debug mode?

I am running in debug mode.

Does the callback also fire twice, if you don’t run in debug mode?

Yep, I just tested it.

If you think it’s a bug, then I can submit if you’d like.

Could you post an MWE demonstrating the issue?

I apologize for not including an MWE to start with.

Here’s the code:

import dash_mantine_components as dmc
from dash import (
    Dash,
    Input,
    Output,
    State,
)
import dash_extensions as de


app = Dash(__name__)


def layout():
    return dmc.Container(
        [
            dmc.ScrollArea(
                dmc.Container(
                    id="chat-history",
                ),
                type="always",
                mt=30,
                offsetScrollbars=True,
                h=700,
                style={
                    "border": f"1px solid {dmc.theme.DEFAULT_COLORS['gray'][8]}",
                    "border-radius": "10px",
                },
            ),
            de.Keyboard(
                dmc.TextInput(
                    placeholder="Press the Enter key to send a message...",
                    id="message",
                    mt=35,
                    w=830,
                ),
                captureKeys=["Enter"],
                id="enter-key-listener",
            ),
        ],
    )


@app.callback(
    Output("chat-history", "children"),
    Output("message", "value"),
    Input("enter-key-listener", "n_keydowns"),
    State("message", "value"),
    State("chat-history", "children"),
    prevent_initial_call=True,
)
def process_message(_, message, children):
    chat_history = [] if not children else children
    chat_history.append(
        dmc.Paper(
            [
                dmc.Text("Message Recipient:", weight="bold"),
                dmc.Text(
                    "I'm sorry, I don't yet know how to respond. Here's a bunch of text to see if the text wraps and makes the container grow."
                ),
            ],
            radius="md",
            p="lg",
            withBorder=True,
            mt=15,
            w=500,
            style={
                "color": "#25282a",
                "background-color": "#03DAC5",
                "float": "right",
            },
        )
    )
    print("message: ", message)
    return [chat_history, ""]


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

Here’s the command line output:

image

And here’s the rendered output:

Here’s some odd observations that I swear didn’t happen from my first test:

  • The second “call” sometimes has the message value and sometimes it doesn’t.
  • Sometimes the rendered output duplicates and sometimes it doesn’t.
  • Sometimes they both duplicate.

Interesting - I can confirm that i am able to reproduce the behavior of the callback firering twice. I agree - it looks like a bug.

It was indeed a bug in the component. I have just fixed it, and pushed a new 1.0.9 release. If you upgrade,

pip install dash-extensions==1.0.9

the issue should be resolved. Please let me know how it goes :slight_smile:

EDIT: I have also added som docs now Dash

1 Like

Oh, thanks so much! That worked like gangbusters.

1 Like