dash-ag-grid
dash-ag-grid copied to clipboard
Suppress chained callback trigger
Hi, there is another issue that comes with the v31.0.0 update, which may or may not be related to https://github.com/plotly/dash-ag-grid/issues/267
A minimal example is as follows: Whenever add row is clicked, the callback is first triggered by the button click, then again triggered by selectedRows. Is there any way to suppress the second callback trigger? This doesn't seem very intuitive and only exists for the new version release. Thanks!
import dash_ag_grid as dag
import pandas as pd
from dash import dcc, html, Input, Output, no_update, State
import dash
import dash_bootstrap_components as dbc
# Sample data
data = {'Name': ['John', 'Jane', 'Bob', 'Alice'],
'Age': [25, 30, 35, 28],
'City': ['New York', 'San Francisco', 'Los Angeles', 'Chicago']}
df = pd.DataFrame(data)
app = dash.Dash(__name__)
app.layout = html.Div([dag.AgGrid(
id='my_table',
columnDefs=[
{'headerName': 'Name', 'field': 'Name', "checkboxSelection": True},
{'headerName': 'Age', 'field': 'Age'},
{'headerName': 'City', 'field': 'City'}
],
rowData=df.to_dict('records'),
defaultColDef={"resizable": True, "sortable": True, "filter": True, "wrapText": True,
"autoHeight": True, "cellStyle": {'textAlign': 'left', 'whiteSpace': 'pre-wrap'}},
columnSize="sizeToFit",
className="ag-theme-alpine selection",
dashGridOptions={
"rowSelection": "single",
"suppressNoRowsOverlay": "true",
},
getRowId="params.data.Name",
style={"height": "400px"}
),
dbc.Button("add row", id='add_row', n_clicks=0)
])
@dash.callback(
Output(component_id='my_table', component_property='rowData'),
Output(component_id="my_table", component_property="selectedRows"),
Input(component_id="my_table", component_property="selectedRows"),
Input(component_id='add_row', component_property='n_clicks'),
State(component_id='my_table', component_property='rowData'),
config_prevent_initial_callbacks=True
)
def update_qa_area(selected_row, add_row, data):
trigger_id = dash.callback_context.triggered[0]['prop_id'].split('.')[0]
trigger_property = dash.callback_context.triggered[0]['prop_id'].split('.')[1] if trigger_id else ""
print(f"Callback triggered: {trigger_id}, {trigger_property}, {selected_row}")
print(f"Output: {selected_row}")
if trigger_id == 'add_row':
selected_row = [{"Name": "", "Age": 0, "City": ""}]
data.append(selected_row[0])
return data, selected_row
if __name__ == '__main__':
app.run_server(debug=True, port='1234')
Hello @yxc8 ,
I'm still unsure as to what you are after here, why do you have an input from the selectedRows, isn't it just populating and re selecting the data when you are altering the selectedRows.
If you want to maintain the selections and get a new selection, I recommend using the Patch method from dash and again, I recommend using the rowTransaction instead of readding the rowData and having everything rerender.
Also, I was thinking this should be a state call of selectedRows instead of an input, as the input seems to be unused.
Basically, there is no point in having an actual input trigger from the selectedRows... Another thing, you could use Patch for the rowData too.
Hi @BSd3v , thanks for the reply. It looks like some context got lost in the minimal example so let me explain a bit more here. The real project I have contains multiple dash ag grid, and a button click is supposed to transfer one row selected in table A to table B and have it selected in table B. The v2.4.0 allows me to set selectedRows and rowData properly and everything works, the new version is causing more than 1 callback being triggered with one click and the second callback triggered de-selects my row.
selectedRows need to be an input to trigger other logics in my callback that also updates the grid, so it cannot be a State. Additionally, using rowTransaction instead of rowData doesn't solve this problem, example below:
import dash_ag_grid as dag
import pandas as pd
from dash import dcc, html, Input, Output, no_update, State
import dash
import dash_bootstrap_components as dbc
# Sample data
data = {'Name': ['John', 'Jane', 'Bob', 'Alice'],
'Age': [25, 30, 35, 28],
'City': ['New York', 'San Francisco', 'Los Angeles', 'Chicago']}
df = pd.DataFrame(data)
app = dash.Dash(__name__)
app.layout = html.Div([dag.AgGrid(
id='my_table',
columnDefs=[
{'headerName': 'Name', 'field': 'Name', "checkboxSelection": True},
{'headerName': 'Age', 'field': 'Age'},
{'headerName': 'City', 'field': 'City'}
],
rowData=df.to_dict('records'),
defaultColDef={"resizable": True, "sortable": True, "filter": True, "wrapText": True,
"autoHeight": True, "cellStyle": {'textAlign': 'left', 'whiteSpace': 'pre-wrap'}},
columnSize="sizeToFit",
className="ag-theme-alpine selection",
dashGridOptions={
"rowSelection": "single",
"suppressNoRowsOverlay": "true",
},
getRowId="params.data.Name",
style={"height": "400px"}
),
dbc.Button("add row", id='add_row', n_clicks=0)
])
@dash.callback(
Output(component_id='my_table', component_property='rowTransaction'),
Output(component_id="my_table", component_property="selectedRows"),
Input(component_id="my_table", component_property="selectedRows"),
Input(component_id='add_row', component_property='n_clicks'),
config_prevent_initial_callbacks=True
)
def update_qa_area(selected_row, add_row):
trigger_id = dash.callback_context.triggered[0]['prop_id'].split('.')[0]
trigger_property = dash.callback_context.triggered[0]['prop_id'].split('.')[1] if trigger_id else ""
print(f"Callback triggered: {trigger_id}, {trigger_property}, {selected_row}")
print(f"Output: {selected_row}")
row_transaction = no_update
if trigger_id == 'add_row':
selected_row = [{"Name": "", "Age": 0, "City": ""}]
row_transaction = {"add": selected_row}
return row_transaction, selected_row
if __name__ == '__main__':
app.run_server(debug=True, port='1234')
Finally, I don't know what I should be patching if I use rowTransaction above, there is no partial update to any output.
To summarize, I am not sure what is the right way to implement the following logic using the new versions of dash-ag-grid: the app contains multiple dash ag grid, and a button click is supposed to transfer one row selected in grid A to grid B and have it selected in grid B. It would be un-intuitive UI for user if I cannot keep the selection after moving the row.
Any suggestions are welcomed, let me know if more context need to be provided. Thank you!
Hi @yxc8
Try changing the last bit of your callback to this:
if trigger_id == 'add_row':
selected_row = [{"Name": f"{add_row}", "Age": 0, "City": ""}]
row_transaction = {"add": selected_row, 'async': False}
return row_transaction, selected_row
- Note that row id, must be unique. I just added the counter from the button as an example.
- In the row transactions, set the async update to
False. See more information in this forum post
Hi @AnnMarieW , thank you for the suggestion! That worked for persisting the row selection, however, when I remove row with rowTransaction and clear the selectedRows accordingly, that still seems to trigger a second callback with the trigger being selectedRows, I also included async is False in that case but didn't help. Any insights on that? Thanks again!
Hello @yxc8,
When we release the newest version, with the selectedRows fix, could you please confirm whether this is fixed. The selectedRows fights with the onSelectionChanged event of the grid, and it quite difficult to get it behaving the way it was initially.
@yxc8 V31.2.0 is released now :tada: Can you confirm it fixes this issue?