Black Lives Matter. Please consider donating to Black Girls Code today.

Manange "enter" event on button

I have a dcc.Input in my dash UI and I would like to trigger a callback once the user press “enter”. I can’t find a way to do it … The available events (‘blur’ and ‘change’) do not help I think (tried both and the first triggers on focus change, the second triggers at each keypress)

I don’t think this is possible at the moment. You can however have the callback be triggered when a user clicks a button:

import dash
from dash.dependencies import Input, State, Output, Event
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()
app.layout = html.Div([
    html.Div(id='target'),
    dcc.Input(id='input', type='text', value=''),
    html.Button(id='submit', type='submit', children='ok'),
])

@app.callback(Output('target', 'children'), [], [State('input', 'value')], [Event('submit', 'click')])
def callback(state):
    return "callback received value: {}".format(state)

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

Unfortunately ‘click’ events are the only events currently supported though. @chriddyp, I was wondering if this would suggest targeting support for ‘submit’ events for forms would be good?

After thinking about all this, I realised that even though the docs for dcc.Input advertises attributes such as minlength and pattern, they’re not currently useful because callbacks targeting input elements need to be currently triggered by changes to value or n_clicks attributes or by a click event – and in both cases they don’t pay any attention to the form validation stuff. If callbacks could target submit events from forms, then we’d get the native browser form validation for free and submitting on enter (since, I believe browsers will implicitly submit a form that consists only of a text field if the user hits enter).

1 Like

(As a side note, can someone tell me how I get Python syntax highlighting on code snippets in the forum?)

Thank you @nedned. However, I want to avoid the user to have to use the mouse (or some “tab” dance) to push the data in the Input to python (my workaround today is indeed having the button).

btw, with the constraint of allowing only one callback per Output, I need to consolidate all events/inputs possibly changing an output in a single callback. But then, to identify what button (when there are multiple buttons) has been clicks in the callback is painful.
Either the callback is triggered through event (“click” on a button) but with multiple events, no clue about which event has been triggered.
Or the callback is triggered through input (“n_clicks” on a button) but then I need to check the counter against the previous counter for each button to see which button triggered the callback (and as you know, using globals is not mutli-session proof so need some serialisation to a DB, gasp!).
So, it would be nicer to have the event as an argument to the callback (with True/False) something like

import dash
from dash.dependencies import Input, State, Output, Event
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()
app.layout = html.Div([
    html.Div(id='target'),
    dcc.Input(id='input', type='text', value=''),
    html.Button(id='submit', type='submit', children='ok'),
    html.Button(id='other-action', children='other action'),
])

@app.callback(Output('target', 'children'), [], [State('input', 'value')], [Event('submit', 'click'),Event('other-action', 'click')])
def callback(state, click_submit, click_other_action):
    if click_other_action: print('other action has been clicked")
    return "callback received value: {}".format(state)

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

But I haven’t yet fully grasped the “dash way” to build my UI …

Yep, you’re right, the downside of the click event is that you don’t know which button triggered the callback. That does seem to be a limitation of Events at the moment.

Edit: You’re quite right, an element can only have the one callback associated with each property. I hadn’t run across this limitation before, I just remember seeing in the Dash source code that multiple callbacks can target the one element, but it seems that’s only if they target different properties – which my suggested code below does not do.

Edit: the following is incorrect…
However the single Output limitation is actually that each callback can only have one target Output. Going the other direction, each element in the DOM can have as many callbacks as you want targeting it. So I think you might be able to do something like this:

for state_id, evend_id in id_pairs:
    @app.callback(Output('target', 'children'), [], [State(state_id, 'value')], [Event(event_id, 'click'))(your_callback_function)

Where id_pairs is a list of tupleseg: [(‘input_1’, ‘submit1’), (‘input_2’, ‘submit2’)…]

Assuming that the same logic in your_callback_function can apply to all input text boxes, which it sounds like is what you’e saying.

The limit AFAIK is that each output can only be in one callback so what you propose does not work (otherwise you get a exceptions.CantHaveMultipleOutputs)

Yeah, I know. Worked this out after my original post. I’m now wondering the reasons for this constraint…

I wonder if you could use jQuery to update a hidden div with the input Field when Enter is keyed. Then you could have a callback that detects changes to that hidden div

Supporting enter is a good idea. Some thoughts:

  • This is traditionally done with <form/> and <input type="submit"/>.
  • I find this pattern a little limiting (correct me if I’m wrong!) because it would force all of your controls to be in the same html.Form container. In a Dash app, you might have controls all over the app (on top of the graph, below the graph, on the side of the app) that might be tied to the same button.
  • Instead, what about using something like accessKey? See “Adding a submit keyboard shortcut” here: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/submit. This suffers from discoverability:

    The problem with the above example is that the user will not know what the access key is! This is especially true since the modifiers are typically non-standard to avoid conflicts. When building a site, be sure to provide this information in a way that doesn’t interfere with the site design (for example by providing an easily accessible link that points to information on what the site access keys are). Adding a tooltip to the button (using the title attribute) can also help, although it’s not a complete solution for accessibility purposes.

    • How would this solution work with multiple buttons?
    • Ideally, we would only trigger the enter action if its related form elements are active. What markup do we use to tell Dash that an input element is related to a submit button? (This is traditionally done with <form/> markup. Are there any alternatives?)

To avoid any misunderstanding, when I am talking about “enter” it is “press the ‘enter’ key when in a dcc.Input (the text entry widget)”. So once the use has finished typing its input and press the ‘enter’ key, we get the callback called.
Something like:

@app.callback(Output('target', 'children'), 
              inputs=[Input('input', 'value_entered')]) 

With Input('input', 'value'), we get a call each time ‘value’ changes, with Input('input', 'value_entered'), we get a call only once the value is confirmed by pressing the ‘enter’ key and we get the ‘value’ as parameter of the callback.
It is really dirty syntax (as we mix a property/value with an event) but you get the idea…

1 Like

Hmmm, I wonder if that’s true. You can combine multiple Events into the one callback. This means that you could have a form tag wrapping each group of controls in different parts of the page, with each form element having a distinct ID, and then in your one callback just target all the submit events using those form IDs. You can also combine Events and Inputs, so a form submit event can be used to trigger the callback, but so can changing the value of some of the inputs – eg a text box you might want to wait until you hit enter before firing the callback, but a dropdown should fire straightaway.

I like the idea of supporting form submit events as it means the standard semantics of forms works out of the box and its closer to the native HTML way of submitting forms. This means we’d get form validation (!) which as I mentioned is essentially broken currently.

@nedned besides the standard semantics, does using the Form approach bring some value/features ?
I rather like the GUI based approach to manage widgets and clock (Interval) than a “submit Form” approach (even if I admit I am a noob in html :wink: )

Here are the advantages I see of using form elements combined with Dash supporting the submit event:

  1. We would be able to selectively target different groups of one or more form controls through Event callback triggers (as many different groups as you want for each callback). Assuming, of course, that the submit events can be associated with the ID of their enclosing form element.

  2. We would get native form validation which is supported by all modern browsers. For example, in the following layout fragment, the browser prevents you from submitting a form with inputs that are outside of the the range 10-20.

      html.Form(id="form", children=[
         dcc.Input(id="foo", type="number", min="10", max="20"),
         dcc.Input(id="submit", type="submit", value="Submit")
     ])
    

    Currently, we can’t take advantage of this validation with Dash. You still see the popup being triggered telling you that the input is invalid, but callbacks that are either listening for changes to the value attribute or click events (the only two methods we have available I think) will ignore this and fire anyway. There’s a range of attributes that can be used for validation, which Dash documents but, as far as I can tell, can’t use: https://plot.ly/dash/dash-core-components/input

  3. You would get the behaviour of triggering a callback on hitting the enter key (the motivating feature of this thread) for free, without even needing to explicitly support an enter keypress event. This is because when hitting enter in an input element, browsers will try to submit the enclosing form element. You wouldn’t even need to include a submit button. I’m pretty sure that the solution to your problem could just be solved with the following layout fragment, if you included an Event with ID foo_form and a State with ID foo in your callback:

    html.Form(dcc.Input(id="foo", type="text"), id="foo_form")

Even if another solution were to be canvased, such as supporting keypress events, why not add support for submit events anyway? Then those who want to use form elements can take advantage of these affordances.

What do you think @chriddyp?

great! I learned a lot, tx @nedned!

This is a feature that I would personally find useful. I’m working on an app that uses a text input as a search box, and pressing the return key instead of clicking a button is a much more natural way of submitting a search. It’s not feasible for me to use events triggered by changes in the value of the input because it causes my app to slow to a crawl (the search updates a table with many entries.)

Ditto this feature request. It is the most intuitive use of dcc.Input. This is one of the first things beta testers note about my app: “Why doesn’t the search box respond to Enter?”

6 Likes

Count me in for this feature as well. Is this possible yet with any work-arounds?

It isn’t. It would be a relatively simple addition to the component, I’ve outlined the solution here: https://github.com/plotly/dash-core-components/issues/230#issuecomment-403340421

Is there a current outlook on this concept? :slight_smile:

There is not, but a PR that implements the solution I mentioned above would be accepted.