Hi,
Out of curiosity, is it possible to dynamically span rows in Dash AgGrid? All the examples I am seeing are hard-coded on number of rows to span. Thanks in advance.
Hi,
Out of curiosity, is it possible to dynamically span rows in Dash AgGrid? All the examples I am seeing are hard-coded on number of rows to span. Thanks in advance.
Yes, it’s possible. The rowSpan
prop accepts a function. Can you say more about how you would like to specify the number of rows to span?
For a table with columns “Student” and 5 other columns corresponding to test scores I would like the student column to span however many rows are present for the specific student i.e the rows represent different years. How can I do this dynamically since not all students have the same number of rows?
Are you ready for this?
Check this out, this will support sorting and filters:
app.py
import dash_ag_grid as dag
from dash import Dash, html, Output, Input
app = Dash(__name__)
data = [
{
'localTime': '5:00am',
'show': {'name': 'Wake Up Dublin', 'presenter': 'Andrew Connell'},
'a': 0.231, 'b': 0.523, 'c': 0.423, 'd': 0.527,
},
{'localTime': '5:15am',
'show': {'name': 'Wake Up Dublin', 'presenter': 'Andrew Connell'},'a': 0.423, 'b': 0.452, 'c': 0.523, 'd': 0.543},
{'localTime': '5:30am', 'show': {'name': 'Wake Up Dublin', 'presenter': 'Andrew Connell'},'a': 0.537, 'b': 0.246, 'c': 0.426, 'd': 0.421},
{'localTime': '5:45am', 'show': {'name': 'Wake Up Dublin', 'presenter': 'Andrew Connell'},'a': 0.893, 'b': 0.083, 'c': 0.532, 'd': 0.983},
{
'localTime': '6:00am',
'show': {'name': 'Pure Back In The Day', 'presenter': 'Kevin Flanagan'},
'a': 0.231, 'b': 0.523, 'c': 0.423, 'd': 0.527,
},
{'localTime': '6:15am', 'a': 0.423, 'b': 0.452, 'c': 0.523, 'd': 0.543, 'show': {'name': 'Pure Back In The Day', 'presenter': 'Kevin Flanagan'},},
{'localTime': '6:30am', 'a': 0.537, 'b': 0.246, 'c': 0.426, 'd': 0.421, 'show': {'name': 'Pure Back In The Day', 'presenter': 'Kevin Flanagan'},},
{'localTime': '6:45am', 'a': 0.893, 'b': 0.083, 'c': 0.532, 'd': 0.983, 'show': {'name': 'Pure Back In The Day', 'presenter': 'Kevin Flanagan'},},
{
'localTime': '7:00am',
'show': {'name': 'The Queens Breakfast', 'presenter': 'Tony Smith'},
'a': 0.231, 'b': 0.523, 'c': 0.423, 'd': 0.527,
},
{'localTime': '7:15am', 'a': 0.423, 'b': 0.452, 'c': 0.523, 'd': 0.543, 'show': {'name': 'The Queens Breakfast', 'presenter': 'Tony Smith'},},
{'localTime': '7:30am', 'a': 0.537, 'b': 0.246, 'c': 0.426, 'd': 0.421, 'show': {'name': 'The Queens Breakfast', 'presenter': 'Tony Smith'},},
{'localTime': '7:45am', 'a': 0.893, 'b': 0.083, 'c': 0.532, 'd': 0.983, 'show': {'name': 'The Queens Breakfast', 'presenter': 'Tony Smith'},},
{
'localTime': '8:00am',
'show': {'name': 'Cosmetic Surgery', 'presenter': 'Niall Crosby'},
'a': 0.231, 'b': 0.523, 'c': 0.423, 'd': 0.527,
},
{'localTime': '8:15am', 'a': 0.423, 'b': 0.452, 'c': 0.523, 'd': 0.543, 'show': {'name': 'Cosmetic Surgery', 'presenter': 'Niall Crosby'},},
{'localTime': '8:30am', 'a': 0.537, 'b': 0.246, 'c': 0.426, 'd': 0.421, 'show': {'name': 'Cosmetic Surgery', 'presenter': 'Niall Crosby'},},
{'localTime': '8:45am', 'a': 0.893, 'b': 0.083, 'c': 0.532, 'd': 0.983, 'show': {'name': 'Cosmetic Surgery', 'presenter': 'Niall Crosby'},},
{
'localTime': '8:00am',
'show': {'name': 'Brickfield Park Sessions', 'presenter': 'Bricker McGee'},
'a': 0.231, 'b': 0.523, 'c': 0.423, 'd': 0.527,
},
{'localTime': '8:15am', 'a': 0.423, 'b': 0.452, 'c': 0.523, 'd': 0.543, 'show': {'name': 'Brickfield Park Sessions', 'presenter': 'Bricker McGee'},},
{'localTime': '8:30am', 'a': 0.537, 'b': 0.246, 'c': 0.426, 'd': 0.421, 'show': {'name': 'Brickfield Park Sessions', 'presenter': 'Bricker McGee'},},
{'localTime': '8:45am', 'a': 0.893, 'b': 0.083, 'c': 0.532, 'd': 0.983, 'show': {'name': 'Brickfield Park Sessions', 'presenter': 'Bricker McGee'},},
]
columnDefs = [
{'field': 'localTime'},
{
'field': 'show',
'cellRenderer': "RowSpanningComplexCellRenderer",
'rowSpan': {"function": "rowSpanningComplex(params, 'show')"},
'cellClassRules': {"show-cell": "params.value && params.data.rowSpanning"},
"width": 300
},
{'field': 'a'},
{'field': 'b'},
{'field': 'c'},
{'field': 'd'},
{'field': 'rowSpanning', 'hide': True}
]
app.layout = html.Div(
[
dag.AgGrid(
id="grid",
rowData=data,
columnDefs=columnDefs,
defaultColDef={"resizable": True, 'sortable': True, 'filter': True, 'flex': 1},
dashGridOptions={"suppressRowTransform": True},
),
],
)
app.clientside_callback(
"""async function () {
var api = await dash_ag_grid.getApiAsync("grid")
api.redrawRows();
return dash_clientside.no_update
}""",
Output("grid", "id"),
Input("grid", "virtualRowData"),
)
if __name__ == "__main__":
app.run(debug=True)
js file
var dagcomponentfuncs = (window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {});
dagfuncs.rowSpanningComplex = function (params, key) {
spanning = 0
var found;
var firstFound;
var included;
params.api.forEachNodeAfterFilterAndSort((node) => {
if (JSON.stringify(node.data[key]) == JSON.stringify(params.data[key]) || params.node.rowIndex == node.rowIndex) {
if (!found && found != 0 && !included) {
found = node.rowIndex
firstFound = node.rowIndex
if (found == params.node.rowIndex) {
included = true
}
spanning++
} else {
if ((node.rowIndex - found) == 1) {
found = node.rowIndex
if (found == params.node.rowIndex) {
included = true
}
spanning++
}
}
}
else if (!included) {
found = null
spanning = 0
}
})
params.node.setDataValue('rowSpanning', included ? (firstFound == params.node.rowIndex ? spanning || 1 : 0) : 1)
return included ? (firstFound == params.node.rowIndex ? spanning || 1 : 0) : 1;
}
var dagcomponentfuncs = (window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {});
dagcomponentfuncs.RowSpanningComplexCellRenderer = function (props) {
let children;
if (props.value && props.data.rowSpanning) {
children = [
React.createElement('div', {className: 'show-name'}, props.value.name),
React.createElement('div', {className: 'show-presenter'}, props.value.presenter),
]
}
return React.createElement('div', null, children)
}
css file
.show-cell {
background: #119dff;
border-left: 1px solid lightgrey !important;
border-right: 1px solid lightgrey !important;
border-bottom: 1px solid lightgrey !important;
}
.show-name {
font-weight: bold;
}
.show-presenter {
font-style: italic;
}
Thanks for your reply. However, i tried replicating the above only to get the below…Am I missing something?
You need to be using the newest version of dash ag grid, this api was added recently.
Updated the DAG version to 2.3.0 and even updated my version of dash but still getting the same result
Same thing about the function doesn’t exist?
You need to make sure you have the Js files in your asset folder, along with the css file. Then it should look the same.
Already do…is there a specific naming convention for these files?
Just checking, but did you refresh the page?
You got the naming convention right. The CSS files just need to end with .css
and JavaScript .js
I ran the example @jinnyzor posted and it worked just like in the images. To ensure it’s actually running the correct version, try including:
print(dag.__version__)
I’ve been trying to get this to work. I have confirmed I am using DAG 31.20 and Dash 2.18.1.
I’m using the exact code above except I fixed an error in the javascript posted since dagfuncs
is not defined.
This error message accumulates in the browser console due to cellClassRules
.
Also, filtering does not work. When filtering resolves to no visible rows, it’s fine. But once it finds a match, it essentially resets the grid showing all rows and clearing the filter.
Hi @bhofmei
Check out this example in the docs:
Note also that while it’s possible to sort and filter while using row spanning, it’s not recommended:
- Sorting and filtering will provide strange results when row spanning. For example, a cell may span 4 rows, however, applying a filter or a sort will probably change the requirements of what rows should be spanned.
Thanks. I saw that in the documentation but thought the code in the above example bypassed the issues with sorting/filtering.