Create reactive dataframe

Hello there! and thanks for this amazing package!

I apologize if this is obvious, but I am trying create (and show) a dataframe where clicking on a given row would replace the current dataframe with another one. That is, each row could trigger the creation of a different dataframe.

Can we do this in dash?
Thanks!

Hi @wizard,

Welcome to the community!

Yes, it is possible, even though click events are not really part of the DataTable component.

Please take a look on this old post that is pretty much related to what you want to do. The only difference is that you need to use as Output the data property of dash_table.DataTable. The data format in active_cell is explained in the DataTable reference.

As a second alternative, you can look into this recent post. It uses an additional library (dash-extensions), however it is arguably a better UX to use a double click to change the data.

1 Like

Thanks!! @jlfsjunior this is super useful.

I tried your second suggestion and (after a few changes in the code) was able to make it run.

import dash
import dash_table
from dash.dependencies import Output, Input
import dash_html_components as html

import pandas as pd
import dash_bootstrap_components as dbc
import json

from dash.exceptions import PreventUpdate

from dash_extensions import EventListener

df = pd.read_csv("https://git.io/Juf1t")
df["id"] = df.index

app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])

table = dash_table.DataTable(
    id="tbl",
    data=df.to_dict("records"),
    columns=[{"name": i, "id": i} for i in df.columns],
)

listen_table = html.Div(
    [
        EventListener(
            id="el",
            events=[{"event": "dblclick", "props": ["srcElement.className", "srcElement.innerText"]}],
            logging=True,
            children=table,
        )
    ]
)

app.layout = dbc.Container(
    [
        dbc.Label("Click a cell in the table:"),
        listen_table,
        dbc.Alert("Click the table", id="out"),
        html.Div(id="event"),
    ]
)


@app.callback(Output("event", "children"), Input("el", "event"), Input("el", "n_events"))
def click_event(event, n_events):    
    # Check if the click is on the active cell.
    if not event or "cell--selected" not in event["srcElement.className"]:
        raise PreventUpdate
    # Return the content of the cell.
    return f"Cell content is {event['srcElement.innerText']}, number of double clicks {n_events}"


@app.callback(Output("out", "children"), Input("tbl", "active_cell"))
def update_graphs(active_cell):
    return json.dumps(active_cell)


if __name__ == "__main__":
    app.run_server(debug=True, port=7676)

However, this does not solve my initial issue: how can I trigger the display of another dataframe when the user clicks on a row? I am not sure how can I create these conditional click events: if click on row 1, then show (random) dataframe1, if click on row 2, then show (random) dataframe 2, etc.

Do you have any ideas?
Thanks!!

If you want to replace the data in the same DataTable you are selecting, you can e.g. replace your second callback by:

@app.callback(
    Output('tbl', 'data'),
    Output('tbl', 'columns'),
    Input('tbl', 'active_cell'))
def update_graphs(active_cell):
    selected_row = active_cell["row"]

    # this is your logic
    if selected_row == 0:
        selected_df = df0
    elif selected_row == 1:
         selected_df = df1
    # and so on...
    
     return selected_df.to_dict("records"), [{"name": i, "id": i} for i in selected_df.columns]

Just note that while this work, it would trigger the same callback once a cell is selected in the new dataframe, so it is a good idea to consider it carefully as a valid user interaction. If it was me, I would show a separated datatable when a row is selected, so it is easy to change the selection.

2 Likes