Click event for button?


#1

I added a button to my layout. When I try to write a callback for it, I get the following error:

dash.exceptions.NonExistantEventException: 
Attempting to assign a callback with
the event "click" but the component
"get_custom_vndr" doesn't have "click" as an event.

Here is a list of the available events in "get_custom_vndr":
[]

Here’s how I’m adding it to my layout:

app_vndr.layout = html.Div([
    html.Button(
        '+',
        id='get_custom_vndr',
        type='submit'
    )
])

and here’s the callback function that’s giving the above error:

@app_vndr.callback(
    dash.dependencies.Output('overlay', 'className'),
    events=[dash.dependencies.Event('get_custom_vndr', 'click'),
            dash.dependencies.Event('add_vndr_id_submit', 'click')])
def show_input(b1_n, b2_n):    
    if b1_n>0:
        return ''
    elif b1_n>0:
        return 'hidden'

Did I miss something when I added the button to my layout? or when I tried to write the callback?

I got it working for

dash.dependencies.Input('get_custom_vndr', 'n_clicks')

but I’d like to use two buttons for the same output and with the n_clicks event, I’d need to try to figure out which button was clicked by comparing the current n_clicks to the previous n_clicks for each button, which seems like a pretty hacky way to do it.


#2

That is the only way to do it right now, Event's won’t help you here.


#3

Event’s won’t help you here

Could you explain that further? Is it something that’s in the pipeline and just isn’t done yet?


#4

Events just trigger a callback, they don’t tell you which event was fired. Yes, we may improve this in the future.


#5

I’m having trouble utilizing a html.Button to trigger a callback using the Event function. Can anyone provide a simple reproducible example? Ideally, I would like to use the click button to select certain rows of a dataframe multiple times before downloading it. Thanks.


#6

For now, use the n_clicks property to determine if a button was clicked. See the Button example in the docs: https://dash.plot.ly/dash-core-components/


#7

Using a local variable I managed to sort which of the two buttons triggered the callback, I Don’t know what problems it might create having the variable only locally, but at least for one user it works :slight_smile:

# In[]:
# Import required libraries
import dash
import dash_html_components as html
from dash.dependencies import Input, Output


# Define the app
app = dash.Dash('')
server = app.server
app.config.suppress_callback_exceptions = False
app.scripts.config.serve_locally = True


class DashCallbackVariables:
    """Class to store information useful to callbacks"""

    def __init__(self):
        self.n_clicks = {1: 0, 2: 0}

    def update_n_clicks(self, nclicks, bt_num):
        self.n_clicks[bt_num] = nclicks


callbacks_vars = DashCallbackVariables()

root_layout = html.Div(
    id='main_page',
    children=[
        html.Div(
                id='btn_name',
                children="No button was clicked"
        ),
        html.Div(
            [
                html.Button(
                    'Button1',
                    id='btn_1',
                    type='submit'
                )
            ]
        ),
        html.Div(
            [
                html.Button(
                    'Button2',
                    id='btn_2',
                    type='submit'
                )
            ]
        )
    ]
)

app.layout = root_layout


@app.callback(
    Output('btn_name', 'children'),
    [
        Input('btn_1', 'n_clicks'),
        Input('btn_2', 'n_clicks'),
    ]
)
def btn_click_callback(nclick_bt1, nclick_bt2):

    # If you have more than one Input, then you need to add this
    # otherwise before the button is clicked its value by default is None
    if nclick_bt1 is None:
        nclick_bt1 = 0
    if nclick_bt2 is None:
        nclick_bt2 = 0

    answer = "No button was clicked"

    if nclick_bt1 != callbacks_vars.n_clicks[1]:
        # It was triggered by a click on the button 1
        callbacks_vars.update_n_clicks(nclick_bt1, 1)

        answer = "Button 1 was clicked"

    if int(nclick_bt2) != callbacks_vars.n_clicks[2]:
        # It was triggered by a click on the button 2
        callbacks_vars.update_n_clicks(nclick_bt2, 2)

        answer = "Button 2 was clicked"

    return answer


# In[]:
# Main
if __name__ == '__main__':
    app.run_server(debug=True)

#8

This type of local mutable state is not recommended as it is not safe to use across multiple processes or workers. For more information on why this is unsafe, see “Why global variables will break your app”: https://dash.plot.ly/sharing-data-between-callbacks

For buttons, use the n_clicks_timestamp property and compare those properties across buttons.