✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐍Plotly, Coiled CEOs Discuss Partnering & ML Experts show us how Dash + Dask apps work Register for the Webinar.

How to distinguish between a button click and a table cell click?

I have a button and I have a DataTable with cells values populated from a Pandas DataFrame. How do I tell if I have clicked on a button or on the table cell? I need if-else in Python that can distinguish between the two. I tried with n_clicks and n_clicks_timestamp but it didn’t work, click on the table cells seems to trigger n_clicks and n_click_timestamp too so I can’t distinguish between them. Any help is appreciated.

There’s no attribute ‘n_clicks’ or ‘n_clicks_timestamp’ for DataTable (according to https://dash.plot.ly/datatable/reference), so why would click the table trigger a click event? It would be great if you can explain a little more.

This is the code responsible for adding new row (which inserts into a database) and updating a table cell (which updates the database). Every time I click on the ‘Add row’ button an update in ‘if’ is being run too which I don’t want to. Click on ‘Add new row’ button should only execute ‘elif’ part of the code but for that I need to distinguish between clicks. The way I have it at the moment doesn’t do that. I tried to extract adding new row into a new function but for that I need to have the same output Output(‘my-datatable’, ‘rows’) and Dash doesn’t support it. Is there any other way to do this? I am using data-table-experiments.

import dash_table_experiments as dt
from dash.dependencies import Input, Output, State

app.layout = html.Div(children=[
dt.DataTable(
    id='my-table',
    rows=df.sort_values('id').to_dict('records'),
    editable=True,
    row_update=True,
    filterable=True,
    sortable=True,
    row_selectable=True
),
html.Button('Add Row', id='add-row-button', n_clicks=0)])

@app.callback( 
Output('my-table', 'rows'),
[Input('my-table', 'row_update'),
Input('add-row-button', 'n_clicks')],
[State('my-table', 'rows')])
def update_rows(row_update, n_clicks, rows):
    if (type(row_update) != bool):
        value = list(row_update[0]['updated'].values())[0]
        column = list(row_update[0]['updated'].keys())[0]
        row_index = row_update[0]['from_row']

        query=f"""BEGIN
        UPDATE my_table
        SET  {column}='{value}'
        WHERE id={row_index + 1}
      END;"""

        cursor.execute(query)
        connection.commit()

        get_query = "SELECT * FROM my-table ORDER BY id"
        rows = pd.read_sql(get_query, connection)
        rows = rows.sort_values('id').to_dict('records')

    # else if 'add new row' button has been clicked, 
    # insert a new empty row in a database which will show as a new row 
    # with index number on a DataTable and treat it as an update later

    elif n_clicks:
        query_insert = f"""BEGIN
                  INSERT INTO my-table
                  DEFAULT VALUES
               END;"""

        cursor.execute(query_insert)
        connection.commit()

        get_query = "SELECT * FROM my-table ORDER BY id"
        rows = pd.read_sql(get_query, connection)
        rows = rows.sort_values('id').to_dict('records')

    return rows

If this isn’t clear, please let me know so I can explain further.

Not sure if I understand correctly but I think the “if” part is triggered after updating values in the table, not by the button. So maybe your problem is after updating values, if would always trigger the “if” part? For example if you want to add a row, then edit a cell value, and doing this over and over again then maybe adding len(row_update)>n_clicks would help.

@app.callback(
Output('my-table', 'rows'),
[Input('my-table', 'row_update'),
Input('add-row-button', 'n_clicks')],
[State('my-table', 'rows')])
def update_rows(row_update, n_clicks, rows):
    if (type(row_update) != bool) and len(row_update)>n_clicks:
        ...

    elif n_clicks:
        ...

    return rows

Thanks for this. At the moment it works for entering a new row but when I go and update any other old row cells after I have added the new row, then the new row is being created too nevertheless, without me even clicking on the ‘Add new button’. So updating old cells keeps adding new rows as if I have clicked on the ‘Add new row’ button which I didn’t.

UPDATE:

Actually when the page just loads the row_update value is a bool type and is True. When I start typing inside the table it changes into row_update [{'from_row': 1016, 'to_row': 1016, 'updated': {'col_name': 'my-new-enetered--cell-value'}}]. So len(row_update) > n_clicks will not make sense in this case. n_clicks is the number of clicks I am clicking on the ‘Add new row’ button and from_row and to_row inside row_update are the index of the row being updated.