Hi,
I’m struggling with updating data that is inside an inactive dcc.Tab
which. Be default dcc.Tabs
seem to inherit some sort of lazy loading that is only applied after the data containing tab is activated (opened) . A similar thread did not solve this issue yet.
In my application I’m trying to make dag.AgGrid
rowData
persistent, which is not natively supported. Therefore I’ve created callbacks that save the rowData
in a local persistent dcc.Store
that can be used to reload the grids. In my architecture the grids are in separate dcc.Tabs
.
I am ultimately looking for a way to make the tabs update, even if they are not open (active). Of course, one could open and reload all tabs one after the other as a workaround but I don’t accept this yet as the only solution In some other frameworks there is a renderActiveOnly
option, so I think there should be more to it.
Here is my test app:
from dash import Dash, html, dcc, callback
from dash.exceptions import PreventUpdate
from dash.dependencies import Input, Output, State
import dash_ag_grid as dag
import dash_bootstrap_components as dbc
app = Dash(__name__)
grid_1_col_defs = [
{"headerName": "Type", "field": "type"},
{"headerName": "Price", "field": "price"},
]
grid_1_row_data = [
{"type": "Black tea", "price": 1},
{"type": "Green tea", "price": 2},
]
grid_1 = dag.AgGrid(
id="grid-1",
columnDefs=grid_1_col_defs,
rowData=grid_1_row_data,
)
grid_1_memory = dcc.Store(id="grid-1-memory", storage_type="local", data=[])
grid_2_col_defs = [
{"headerName": "First name", "field": "first_name"},
{"headerName": "Last name", "field": "last_name"},
]
grid_2_row_data = [
{"first_name": "Peter", "last_name": "Griffin"},
{"first_name": "Marge", "last_name": "Simpson"},
]
grid_2 = dag.AgGrid(
id="grid-2",
columnDefs=grid_2_col_defs,
rowData=grid_2_row_data,
)
grid_2_memory = dcc.Store(id="grid-2-memory", storage_type="local", data=[])
app.layout = html.Div(
[
dbc.Button("Add row to grids", id="btn-add-row"),
dbc.Button("Reload grids", id="btn-reload-grids"),
dbc.Button("Show memory", id="btn-show-memory"),
dcc.Tabs(
[
dcc.Tab(label="Tab 1", children=grid_1),
dcc.Tab(label="Tab 2", children=grid_2),
],
),
html.Div(id="memory-output"),
grid_1_memory,
grid_2_memory,
]
)
@callback(
Output("grid-1", "rowTransaction"),
Output("grid-2", "rowTransaction"),
Input("btn-add-row", "n_clicks"),
prevent_inital_call=True,
)
def add_row(n_clicks):
if n_clicks:
return {"add": [{"type": "Jasmin tea", "price": 3}]}, {
"add": [{"first_name": "Donald", "last_name": "Duck"}]
}
else:
raise PreventUpdate
@callback(
Output("grid-1-memory", "data"),
Output("grid-2-memory", "data"),
Input("grid-1", "rowData"),
Input("grid-2", "rowData"),
prevent_inital_call=True,
)
def save_memory(_grid_1_row_data, _grid_2_row_data):
return _grid_1_row_data, _grid_2_row_data
@callback(
Output("grid-1", "rowData"),
Output("grid-2", "rowData"),
Input("btn-reload-grids", "n_clicks"),
State("grid-1-memory", "data"),
State("grid-2-memory", "data"),
prevent_inital_call=True,
)
def reload_grids(n_clicks_reload, grid_1_memory_data, grid_2_memory_data):
if n_clicks_reload:
return grid_1_memory_data, grid_2_memory_data
raise PreventUpdate
@callback(
Output("memory-output", "children"),
Input("btn-show-memory", "n_clicks"),
State("grid-1-memory", "data"),
State("grid-2-memory", "data"),
prevent_inital_call=True,
)
def show_memory(n_clicks_show, grid_1_memory_data, grid_2_memory_data):
if n_clicks_show:
grid_1_memory_str = "\n".join([f"\t{row}" for row in grid_1_memory_data])
grid_2_memory_str = "\n".join([f"\t{row}" for row in grid_2_memory_data])
return html.Pre(
f"Grid 1 memory:\n{grid_1_memory_str},\nGrid 2 memory:\n{grid_2_memory_str}"
)
else:
raise PreventUpdate
if __name__ == "__main__":
app.run_server(debug=True, port=1234)
As you can see, when adding a row to both grids, they are only really added, when the tab in which they are stored is activated.