Hello @Emil,
Yes, there are a few workarounds.
The props here gives you access to the grid’s api, so you can use it to trigger another event that you can access.
You can pass the clicks to the sorting:
You’d listen to columnState
or virtualRowData
for this.
You could also update a cell value on some hidden column in the first row.
You’d listen to cellValueChanged
for this.
You also have access to the gridOptions, from which you can access some of the grid event listeners:
dagcomponentfuncs.ButtonHeader = function (props) {
return React.createElement(
'button',
{
onClick: () => {
props.api.gridOptionsService.gridOptions.onCellClicked({value: {'headerClicked': props.column.colId},
column: props.column.colId,
rowIndex: null, node: []})},
className: props.className,
style: props.style,
},
props.displayName)
}
As far as setting arbitrary props, this isnt a supported way from the grid, this was discussed a little, but the idea was given that Dash would eventually support this. XD
But… I have a workaround for that too:
function get_setProps(dom, traverseUp = 0) {
const key = Object.keys(dom).find(key=>{
return key.startsWith("__reactFiber$") // react 17+
|| key.startsWith("__reactInternalInstance$"); // react <17
});
const domFiber = dom[key];
if (domFiber == null) return null;
// react <16
if (domFiber._currentElement) {
let compFiber = domFiber._currentElement._owner;
for (let i = 0; i < traverseUp; i++) {
compFiber = compFiber._currentElement._owner;
}
return compFiber;
}
// react 16+
const GetCompFiber = fiber=>{
//return fiber._debugOwner; // this also works, but is __DEV__ only
let parentFiber = fiber.return;
while (typeof parentFiber.type == "string") {
parentFiber = parentFiber.return;
}
return parentFiber;
};
let compFiber = GetCompFiber(domFiber);
for (let i = 0; i < traverseUp; i++) {
compFiber = GetCompFiber(compFiber);
}
if (compFiber.stateNode) {
if (Object.keys(compFiber.stateNode.props).includes('setProps')) {
return compFiber.stateNode.props
}
if (Object.keys(compFiber.stateNode).includes('setProps')) {
return compFiber.stateNode
}
}
for (let i = 0; i < 30; i++) {
compFiber = GetCompFiber(compFiber);
if (compFiber.stateNode) {
if (Object.keys(compFiber.stateNode.props).includes('setProps')) {
return compFiber.stateNode.props
}
if (Object.keys(compFiber.stateNode).includes('setProps')) {
return compFiber.stateNode
}
}
}
throw new Error("this is not the dom you are looking for")
}
Whole thing together:
function get_setProps(dom, traverseUp = 0) {
const key = Object.keys(dom).find(key=>{
return key.startsWith("__reactFiber$") // react 17+
|| key.startsWith("__reactInternalInstance$"); // react <17
});
const domFiber = dom[key];
if (domFiber == null) return null;
// react <16
if (domFiber._currentElement) {
let compFiber = domFiber._currentElement._owner;
for (let i = 0; i < traverseUp; i++) {
compFiber = compFiber._currentElement._owner;
}
return compFiber;
}
// react 16+
const GetCompFiber = fiber=>{
//return fiber._debugOwner; // this also works, but is __DEV__ only
let parentFiber = fiber.return;
while (typeof parentFiber.type == "string") {
parentFiber = parentFiber.return;
}
return parentFiber;
};
let compFiber = GetCompFiber(domFiber);
for (let i = 0; i < traverseUp; i++) {
compFiber = GetCompFiber(compFiber);
}
if (compFiber.stateNode) {
if (Object.keys(compFiber.stateNode.props).includes('setProps')) {
return compFiber.stateNode.props
}
if (Object.keys(compFiber.stateNode).includes('setProps')) {
return compFiber.stateNode
}
}
for (let i = 0; i < 30; i++) {
compFiber = GetCompFiber(compFiber);
if (compFiber.stateNode) {
if (Object.keys(compFiber.stateNode.props).includes('setProps')) {
return compFiber.stateNode.props
}
if (Object.keys(compFiber.stateNode).includes('setProps')) {
return compFiber.stateNode
}
}
}
throw new Error("this is not the dom you are looking for")
}
dagcomponentfuncs.ButtonHeader = function (props) {
return React.createElement(
'button',
{
onClick: () => {get_setProps(event.target.closest('div.ag-theme-alpine')).setProps({'cellClicked': {value: `header clicked ${props.column.colId}`}})},
className: props.className,
style: props.style,
},
props.displayName)
}
app:
import dash_ag_grid as dag
from dash import Dash, html, Input, Output
import pandas as pd
import json
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
app = Dash(__name__)
app.layout = html.Div(
[
dag.AgGrid(
id="column-definition-ID",
rowData=df.to_dict("records"),
columnDefs=[{
"field": "athlete",
"headerName": "Athlete",
"headerComponent": "ButtonHeader" # select custom component
}],
columnSize="sizeToFit",
),
html.Div(id='output')
]
)
@app.callback(Output('output', 'children'),
Input('column-definition-ID', 'cellClicked'))
def sorting(s):
return json.dumps(s)
if __name__ == "__main__":
app.run(debug=True, port=7777)
Now, with all this said, this should technically be an issue on github because it shouldnt be dependent if you have the component in a row.