Good day, All,
Here are a few examples of the new things that you can do with the latest release of Dash AG Grid 31.2+.
eventListeners:
These can be passed as such:
eventListeners={'cellFocused': ['myfunction1(params, setGridProps)']}
Where eventListeners
is a dictionary where the key is the eventListener
that you want to listen to, these can be found here. The value of the key is an array of functions that you want to add to the specific event, this is helpful in the event that you want two separate things to happen when a user interacts with the grid.
These eventListeners
are added upon the grid’s event, gridReady. This means that events will only apply once the grid is ready for them, this is a big change from the previous way that eventListeners
had to be added:
app.clientside_callback(
"""(id) => {
dash_ag_grid.getApiAsync(id).then((grid) => {
grid.addEventListener('cellFocused',
(params) => {
myFunction1(params)
}
)
})
return window.dash_clientside.no_update
}""",
Output('grid', 'id'),
Input('grid', 'id')
)
why this matters?
The above code will work as long as the grid is rendered and ready within 2 minutes of the callback being triggered, however, if you dont have the grid rendered in time, you will receive this in the browser console:
With the new method, you no longer need to wait for the grid to be rendered as these will automatically be loaded when ready. These are important for things like if the grid is loaded into a component that doesnt exist in the DOM tree immediately. (Modals, popovers, offcanvas, drawers, and tabs (depending on configuration))
Example Old
import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State, no_update, Patch
import pandas as pd
import json
import dash_mantine_components as dmc
snippet = dmc.Prism(language="Python",children=
"""import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State, no_update, Patch
import pandas as pd
import json
app = Dash(__name__)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [
{"field": "athlete"},
{"field": "age", "filter": "agNumberColumnFilter", "maxWidth": 100},
{"field": "country"},
{
"headerName": "Date",
"filter": "agDateColumnFilter",
"valueGetter": {"function": "d3.timeParse('%d/%m/%Y')(params.data.date)"},
"valueFormatter": {"function": "params.data.date"},
},
{"field": "sport"},
{"field": "total"},
]
app.layout = html.Div(
[
dmc.Modal([
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
rowData=df.to_dict("records"),
columnSize="sizeToFit",
dashGridOptions={"animateRows": False, 'rowSelection': 'single'},
),
html.Div(id='info'),
],
id='modalTest',
fullScreen=True),
html.Button('Open Modal', id='openModal')
]
)
app.clientside_callback(
""""""(id) => {
dash_ag_grid.getApiAsync(id).then((grid) => {
grid.addEventListener('cellFocused',
(params) => {
params.api.setNodesSelected({nodes: [params.api.getRowNode(params.rowIndex)], newValue: true})
}
)
})
return window.dash_clientside.no_update
}"""""",
Output('grid', 'id'),
Input('grid', 'id')
)
@app.callback(
Output('info', 'children'),
Input('grid', 'selectedRows')
)
def showFilters(d):
if d:
return json.dumps(d)
return no_update
@app.callback(
Output('modalTest', 'opened'),
Input('openModal', 'n_clicks'),
prevent_initial_call=True
)
def openModal(n):
if n:
return True
return no_update
if __name__ == "__main__":
app.run(debug=True, port=1234)"""
)
app = Dash(__name__, title="Modal Old", update_title=False)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [
{"field": "athlete"},
{"field": "age", "filter": "agNumberColumnFilter", "maxWidth": 100},
{"field": "country"},
{
"headerName": "Date",
"filter": "agDateColumnFilter",
"valueGetter": {"function": "d3.timeParse('%d/%m/%Y')(params.data.date)"},
"valueFormatter": {"function": "params.data.date"},
},
{"field": "sport"},
{"field": "total"},
]
app.layout = html.Div(
[
dmc.Modal([
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
rowData=df.to_dict("records"),
columnSize="sizeToFit",
dashGridOptions={"animateRows": False, 'rowSelection': 'single'},
),
html.Div(id='info'),
],
id='modalTest',
fullScreen=True),
html.Button('Open Modal', id='openModal'),
snippet
]
)
app.clientside_callback(
"""(id) => {
dash_ag_grid.getApiAsync(id).then((grid) => {
grid.addEventListener('cellFocused',
(params) => {
params.api.setNodesSelected({nodes: [params.api.getRowNode(params.rowIndex)], newValue: true})
}
)
})
return window.dash_clientside.no_update
}""",
Output('grid', 'id'),
Input('grid', 'id')
)
@app.callback(
Output('info', 'children'),
Input('grid', 'selectedRows')
)
def showFilters(d):
if d:
return json.dumps(d)
return no_update
@app.callback(
Output('modalTest', 'opened'),
Input('openModal', 'n_clicks'),
prevent_initial_call=True
)
def openModal(n):
if n:
return True
return no_update
if __name__ == "__main__":
app.run(debug=True, port=1234)
Example New
import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State, no_update, Patch
import pandas as pd
import json
import dash_mantine_components as dmc
snippet = dmc.Prism(language="Python",children=
"""import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State, no_update, Patch
import pandas as pd
import json
app = Dash(__name__)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [
{"field": "athlete"},
{"field": "age", "filter": "agNumberColumnFilter", "maxWidth": 100},
{"field": "country"},
{
"headerName": "Date",
"filter": "agDateColumnFilter",
"valueGetter": {"function": "d3.timeParse('%d/%m/%Y')(params.data.date)"},
"valueFormatter": {"function": "params.data.date"},
},
{"field": "sport"},
{"field": "total"},
]
app.layout = html.Div(
[
dmc.Modal([
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
rowData=df.to_dict("records"),
columnSize="sizeToFit",
dashGridOptions={"animateRows": False, 'rowSelection': 'single'},
eventListeners={'cellFocused': ["params.api.setNodesSelected({nodes: [params.api.getRowNode(params.rowIndex)], newValue: true})"]}
),
html.Div(id='info'),
],
id='modalTest',
fullScreen=True),
html.Button('Open Modal', id='openModal')
]
)
@app.callback(
Output('info', 'children'),
Input('grid', 'selectedRows')
)
def showFilters(d):
if d:
return json.dumps(d)
return no_update
@app.callback(
Output('modalTest', 'opened'),
Input('openModal', 'n_clicks'),
prevent_initial_call=True
)
def openModal(n):
if n:
return True
return no_update
if __name__ == "__main__":
app.run(debug=True, port=1233)"""
)
app = Dash(__name__, title="Modal New", update_title=False)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [
{"field": "athlete"},
{"field": "age", "filter": "agNumberColumnFilter", "maxWidth": 100},
{"field": "country"},
{
"headerName": "Date",
"filter": "agDateColumnFilter",
"valueGetter": {"function": "d3.timeParse('%d/%m/%Y')(params.data.date)"},
"valueFormatter": {"function": "params.data.date"},
},
{"field": "sport"},
{"field": "total"},
]
app.layout = html.Div(
[
dmc.Modal([
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
rowData=df.to_dict("records"),
columnSize="sizeToFit",
dashGridOptions={"animateRows": False, 'rowSelection': 'single'},
eventListeners={'cellFocused': ["params.api.setNodesSelected({nodes: [params.api.getRowNode(params.rowIndex)], newValue: true})"]}
),
html.Div(id='info'),
],
id='modalTest',
fullScreen=True),
html.Button('Open Modal', id='openModal'),
snippet
]
)
@app.callback(
Output('info', 'children'),
Input('grid', 'selectedRows')
)
def showFilters(d):
if d:
return json.dumps(d)
return no_update
@app.callback(
Output('modalTest', 'opened'),
Input('openModal', 'n_clicks'),
prevent_initial_call=True
)
def openModal(n):
if n:
return True
return no_update
if __name__ == "__main__":
app.run(debug=True, port=1233)
Event Listeners brings access to setGridProps
When adding an event listener via the new method, you are given access to set props directly from the function without needing to use set_props
or a callback
from the event.
Utilize it as such: setGridProps({'virtualRowData': rows})
where the prop is the key and the value is the value which you would like to set.
Example setGridProps
import dash_ag_grid as dag
from dash import Dash, html
import dash_mantine_components as dmc
snippet = dmc.Prism(language="Python",children="""
import dash_ag_grid as dag
from dash import Dash, html
app = Dash(__name__)
columnDefs = [
{"field": "classNames"},
]
rowData = [
{'classNames': 'ag-theme-alpine'},
{'classNames': 'ag-theme-balham'},
{'classNames': 'ag-theme-material'},
{'classNames': 'ag-theme-quartz'},
{'classNames': 'ag-theme-alpine-dark'},
{'classNames': 'ag-theme-balham-dark'},
{'classNames': 'ag-theme-material-dark'},
{'classNames': 'ag-theme-quartz-dark'},
]
app.layout = html.Div(
[
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
rowData=rowData,
eventListeners={'cellFocused': ['setGridProps({className: params.api.getRowNode(params.rowIndex).data.classNames})']}
),
]
)
if __name__ == "__main__":
app.run(debug=True, port=1236)
""")
app = Dash(__name__, title="setGridProps", update_title=False)
columnDefs = [
{"field": "classNames"},
]
rowData = [
{'classNames': 'ag-theme-alpine'},
{'classNames': 'ag-theme-balham'},
{'classNames': 'ag-theme-material'},
{'classNames': 'ag-theme-quartz'},
{'classNames': 'ag-theme-alpine-dark'},
{'classNames': 'ag-theme-balham-dark'},
{'classNames': 'ag-theme-material-dark'},
{'classNames': 'ag-theme-quartz-dark'},
]
app.layout = html.Div(
[
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
rowData=rowData,
eventListeners={'cellFocused': ['setGridProps({className: params.api.getRowNode(params.rowIndex).data.classNames})']}
),
snippet
]
)
if __name__ == "__main__":
app.run(debug=True, port=1236)
Here is another example of an advanced method for listening to the filterChanged
and storing the advanced filters (AG Grid Enterprise).
In this example, we utilize the new eventListeners
along with a newly available JS function (dash 2.16), set_props
We listen to filterChanged
and store it into a dcc.Store
that can then save the filter to be reapplied later via a dropdown.
Example
import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State, no_update, Patch
import pandas as pd
import json
import dash_mantine_components as dmc
snippet = dmc.Prism(language="Python",children="""
import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State, no_update, Patch
import pandas as pd
import json
app = Dash(__name__)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [
{"field": "athlete"},
{"field": "age", "filter": "agNumberColumnFilter", "maxWidth": 100},
{"field": "country"},
{
"headerName": "Date",
"filter": "agDateColumnFilter",
"valueGetter": {"function": "d3.timeParse('%d/%m/%Y')(params.data.date)"},
"valueFormatter": {"function": "params.data.date"},
},
{"field": "sport"},
{"field": "total"},
]
app.layout = html.Div(
[
dag.AgGrid(
id="filter-options-example-simple",
columnDefs=columnDefs,
rowData=df.to_dict("records"),
columnSize="sizeToFit",
dashGridOptions={"animateRows": False, 'enableAdvancedFilter': True},
enableEnterpriseModules=True,
eventListeners={'filterChanged': ['myFilters(params, setGridProps, "gridFilters")']}
),
dcc.Store(id='gridFilters'),
html.Div(id='filters'),
html.Button('store filter', id='storeFilter'),
dcc.Store(id='storedFilters', data=[]),
dcc.Dropdown(id='storedFilterSelection')
]
)
@app.callback(
Output('filters', 'children'),
Input('gridFilters', 'data')
)
def showFilters(d):
if d:
return json.dumps(d)
return no_update
@app.callback(
Output('storedFilters', 'data'),
Input('storeFilter', 'n_clicks'),
State('gridFilters', 'data'),
prevent_initial_call=True
)
def saveFilter(n, d):
if d:
new = Patch()
new.append({'label': f'{n}', 'value': json.dumps(d)})
return new
return no_update
@app.callback(
Output('storedFilterSelection', 'options'),
Input('storedFilters', 'data')
)
def updateOptions(d):
if d:
return d
return no_update
app.clientside_callback(
""""""(v, id) => {
dash_ag_grid.getApi(id).setAdvancedFilterModel(JSON.parse(v))
return dash_clientside.no_update
}"""""",
Output('filter-options-example-simple', 'id'),
Input('storedFilterSelection', 'value'),
State('filter-options-example-simple', 'id'),
prevent_initial_call=True
)
if __name__ == "__main__":
app.run(debug=True, port=1235)
""")
app = Dash(__name__, title="Advanced Filter", update_title=False)
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
columnDefs = [
{"field": "athlete"},
{"field": "age", "filter": "agNumberColumnFilter", "maxWidth": 100},
{"field": "country"},
{
"headerName": "Date",
"filter": "agDateColumnFilter",
"valueGetter": {"function": "d3.timeParse('%d/%m/%Y')(params.data.date)"},
"valueFormatter": {"function": "params.data.date"},
},
{"field": "sport"},
{"field": "total"},
]
app.layout = html.Div(
[
dag.AgGrid(
id="filter-options-example-simple",
columnDefs=columnDefs,
rowData=df.to_dict("records"),
columnSize="sizeToFit",
dashGridOptions={"animateRows": False, 'enableAdvancedFilter': True},
enableEnterpriseModules=True,
eventListeners={'filterChanged': ['myFilters(params, setGridProps, "gridFilters")']}
),
dcc.Store(id='gridFilters'),
html.Div(id='filters'),
html.Button('store filter', id='storeFilter'),
dcc.Store(id='storedFilters', data=[]),
dcc.Dropdown(id='storedFilterSelection'),
snippet
]
)
@app.callback(
Output('filters', 'children'),
Input('gridFilters', 'data')
)
def showFilters(d):
if d:
return json.dumps(d)
return no_update
@app.callback(
Output('storedFilters', 'data'),
Input('storeFilter', 'n_clicks'),
State('gridFilters', 'data'),
prevent_initial_call=True
)
def saveFilter(n, d):
if d:
new = Patch()
new.append({'label': f'{n}', 'value': json.dumps(d)})
return new
return no_update
@app.callback(
Output('storedFilterSelection', 'options'),
Input('storedFilters', 'data')
)
def updateOptions(d):
if d:
return d
return no_update
app.clientside_callback(
"""(v, id) => {
dash_ag_grid.getApi(id).setAdvancedFilterModel(JSON.parse(v))
return dash_clientside.no_update
}""",
Output('filter-options-example-simple', 'id'),
Input('storedFilterSelection', 'value'),
State('filter-options-example-simple', 'id'),
prevent_initial_call=True
)
if __name__ == "__main__":
app.run(debug=True, port=1235)
js file
var dagfuncs = window.dashAgGridFunctions = window.dashAgGridFunctions || {};
dagfuncs.myFilters = (params, setGridProps, id) => {
if (params.source !== 'advancedFilter') return
const data = JSON.stringify(params.api.getAdvancedFilterModel() || {})
dash_clientside.set_props(id, {data})
}