Learn how to use Dash Bio for next-gen sequencing & quality control. 🧬Register for the Oct 27 webinar.

Unselect active_cell (datatable)

All,
I am trying to implement a dash datatable, where I select rows by a direct click on it (no radio buttons). Currently, I am doing this with the active_cell and it works well:
No matter in which cell of a row a user clicks, a graph is updated with the data in that row. If he clicks another cell in the same row, the data is unselected (via a dcc.storage)

Here comes my problem: If the user clicks the same cell again, there is no active_cell event triggered. Therefore, my callback is not triggered and nothing happens.
I would like to deselect that cell the second time the user clicks it. How can I implement that?

Thanks!

So… I solved this… it is not pretty but it works - it includes a loopbreaker which I had to implement to avoid a circular dependency, but yeah - I am absolutely open for cleaner solutions.

Find below the callbacks

    # takes user selected cell (active_cell) and the current state of a dcc.storage (tableclick) which stores the last saved row that was clicked
# output: updated selected_rows for the datatable, styling for the selected row and update for dcc.storage
# if no cell is selected, do nothing
# if no cell is selected, but there is a row stored as selected, highlight that row (this is a consequence from the circular dependency)
# if a cell is selected that is different from the previous cell, highlight that new row. Otherwise, deselect the row.
@app.callback(
    [Output('performancedatatable', 'style_data_conditional'), Output('tableclick', 'data'),
     Output('performancedatatable', 'selected_rows')],
    [
        Input('performancedatatable', 'active_cell'),
    ], [State('tableclick', 'data')]
)
def highlight_row(cell, prevrow):
    if cell is None:
        if prevrow is None:
            return [{}], None, []
        else:
            return [{}], None, prevrow
    elif "row" in cell:
        if cell.get("row", "") == prevrow:
            return [{}], None, []
        else:
            return ([{
                "if": {"row_index": cell.get("row", "")},
                "backgroundColor": "rgba(146, 192, 234, 0.5)",
            }], cell.get("row", ""), [cell.get("row", "")])


# Is triggered by changing the dcc.storage "tableclick"
# resets active cell and selected cell via callback below
@app.callback([Output('loopbreaker_div', "children")], [Input('tableclick', 'data')])
def reset_active_cell(input):
    return [html.Div(id='loopbreaker', children=True)]


#loopbreaker to avoid circular dependency
@app.callback([Output('performancedatatable', "active_cell"), Output('performancedatatable', 'selected_cells')], [Input('loopbreaker', 'children')])
def reset_active_cell(input):
    time.sleep(1)
    return (None, [])

Shoutout to http://yaaics.blogspot.com/2019/03/circular-references-in-plotlydash.html for helping resolving the circular dependency