@jinnyzor I had flagged your response as the solution but after further testing I’ve realized it doesn’t quite work. Your response works fine as long as the user only clicks on the Load Data button once. But on subsequent clicks the first row will not be highlighted. Presumably this is happening because duplicate data is being added to the table.
Since I don’t want duplicate data, I’ve added a chained callback (based on what you said above I assume I can’t remove data and add data with one transaction?). The first callback clears the table of all data. Then second callback (output_selected_rows
) does all the updating. For reasons I don’t understand, the second callback now becomes an infinite loop, calling itself endlessly.
import dash_ag_grid as dag
from dash import Dash, html, Input, Output, callback, no_update, ctx
import pandas as pd
app = Dash(__name__)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [{"field": i} for i in ["country", "year", "athlete", "age", "sport", "total"]]
app.layout = html.Div(
[
html.Button("Load Data", id="btn-load-data"),
dag.AgGrid(
id="row-selection-selected-rows",
columnDefs=columnDefs,
columnSize="autoSize",
dashGridOptions={"rowSelection": "single", "animateRows": False},
),
html.Pre(id="pre-row-selection-selected-rows", style={'text-wrap': 'wrap'})
],
)
@callback(
Output("row-selection-selected-rows", "rowData"),
Input("btn-load-data", "n_clicks"),
prevent_initial_call=True,
)
def clear_table(_):
return []
@callback(
Output("row-selection-selected-rows", "rowTransaction"),
Output("row-selection-selected-rows", "selectedRows"),
Output("pre-row-selection-selected-rows", "children"),
Input("row-selection-selected-rows", "rowData"),
Input("row-selection-selected-rows", "selectedRows"),
prevent_initial_call=True,
)
def output_selected_rows(_, selected_rows):
print(ctx.triggered_prop_ids)
if "row-selection-selected-rows.rowData" in ctx.triggered_prop_ids.keys():
return {'add': df.to_dict("records"), 'async': False}, df.head(1).to_dict("records"), ""
else:
selected_list = [f"{s['athlete']} ({s['year']})" for s in selected_rows]
msg = f"You selected the athlete{'s' if len(selected_rows) > 1 else ''}:\n{', '.join(selected_list)}" if selected_rows else "No selections"
return no_update, no_update, msg
if __name__ == "__main__":
app.run(debug=True)
I can break this infinite loop by using a Store to trigger the second callback instead of rowData. This is what I’ve coded below. This almost achieves the desired behavior. On the first click of the Load Data button the data loads but output_selected_rows
gets triggered a second time and the first row isn’t shown as selected (because the table has duplicate data?). On all subsequent clicks of the Load Data button the app behaves as I expect: each of the callbacks are only triggered once. Any ideas how to fix this?
import dash_ag_grid as dag
from dash import Dash, html, Input, Output, State, callback, no_update, ctx, dcc
import pandas as pd
app = Dash(__name__)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [{"field": i} for i in ["country", "year", "athlete", "age", "sport", "total"]]
app.layout = html.Div(
[
html.Button("Load Data", id="btn-load-data"),
dag.AgGrid(
id="row-selection-selected-rows",
columnDefs=columnDefs,
columnSize="autoSize",
dashGridOptions={"rowSelection": "single", "animateRows": False},
),
html.Pre(id="pre-row-selection-selected-rows", style={'text-wrap': 'wrap'}),
dcc.Store(id="table-clear-flag")
],
)
@callback(
Output("row-selection-selected-rows", "rowData"),
Output("table-clear-flag", "data"),
Input("btn-load-data", "n_clicks"),
State("table-clear-flag", "data"),
prevent_initial_call=True,
)
def clear_table(_, flag):
return [], not flag
@callback(
Output("row-selection-selected-rows", "rowTransaction"),
Output("row-selection-selected-rows", "selectedRows"),
Output("pre-row-selection-selected-rows", "children"),
Input("table-clear-flag", "data"),
Input("row-selection-selected-rows", "selectedRows"),
prevent_initial_call=True,
)
def output_selected_rows(_, selected_rows):
print(ctx.triggered_id)
if ctx.triggered_id == "table-clear-flag":
return {'add': df.to_dict("records"), 'async': False}, df.head(1).to_dict("records"), ""
else:
selected_list = [f"{s['athlete']} ({s['year']})" for s in selected_rows]
msg = f"You selected the athlete{'s' if len(selected_rows) > 1 else ''}:\n{', '.join(selected_list)}" if selected_rows else "No selections"
return no_update, no_update, msg
if __name__ == "__main__":
app.run(debug=True)