Hey guys! How would you add dbc.Switch() to a DataTable so that a boolean column is visualized by switches instead of “true” or “false”? Is that even possible? Thanks!
At this time, you can’t put components in DataTable cells. A workaround is to use an HTML Table.
Hi @AnnMarieW is there any change regarding this topic? Thanks ![]()
Hey @dashamateur !
Consider switching to ag-grid.
Hey @AIMPED thanks for the reply! I tried avoiding js but i guess it will be necessary ![]()
Hi @dashamateur
In the next major release of Dash AG Grid, the boolean values will automatically render as check boxes. No custom components required ![]()
Until then, you can see examples of how to add components to cells in the docs. If you would like a dbc.Switch instead of a checkbox, you can find an example here:
There shouldn’t be too much JavaScript coding involved if you copy one of the examples, but if you have any questions, feel free to ask for help here ![]()
Hi @AnnMarieW and thanks for the reply! My problem is a bit more specific as I’m trying to render a substring inside a cell. I am currently doing this with an if sentence:
// INPUT
dagcomponentfuncs.DBC_Input = function (props) {
const { type, value, placeholder } = props;
let string = props.data.check;
if (string.includes('INPUT')) {
// Replace 'INPUT' with dbc.Input component
string = string.replace(
'INPUT',
React.createElement(
window.dash_bootstrap_components.Input,
{
type: type || 'text',
value: value || '',
placeholder: placeholder || '0,00',
}
)
);
}
return string
};
which finds the substring INPUT and renders it but it returns [object Object]. I am still trying to find a remedy for that. Do you have any suggestions on how to render specific substrings into dbc components? Thanks alot!
EDIT:
Problem was in the types of data i was returning because it converted dbc.Input to string. After some struggle i managed to get it to work as wanted (code below). I am open to cleaner solutions
Also, how would you capture updated grid to include the user inputs? I have created a button that reads the data: State(“ag-grid”, “rowData”), but it doesn’t include rendered cells. Is there a workaround to “scrape” the grid? Thanks!
// INPUT
dagcomponentfuncs.DBC_Input = function (props) {
const { type,handleInputChange,placeholder } = props;
let parts = props.data.check.split('INPUT');
let elements = [];
// Iterate over parts and add both strings and React elements to the array
parts.forEach((part, index) => {
elements.push(part);
if (index < parts.length - 1) {
// Insert the React element
elements.push(
React.createElement(
window.dash_bootstrap_components.Input,
{
type: type || 'text',
onChange: handleInputChange,
placeholder: placeholder || '0,00',
style: {
display: "inline-block",
width: "20%",
},
}
)
);
}
});
return React.createElement('div', null, elements);
};
For anyone with this problem: rendering part of ag-grid cell this is how i solved it for myself. I wanted to replace part of string in a cell with dbc.Input and keep the components functionality so I would be able to use the users entered data. I achived this with this function in a .js file:
dagcomponentfuncs.DBC_Input_With_String = function (props) {
const { setData, handleInputChange, data } = props;
const [inputValues, setInputValues] = React.useState(Array(props.data.check.split(/(%%.*?%%)/).length).fill(''));
const setProps = (index, inputValue) => {
const combinedValue = data.check.split(/(%%.*?%%)/).map((part, index2) => {
if (index2 % 2 === 0) {
return part;
} else if (index === Math.floor(index2)) {
return `%%${inputValue}%%`;
} else {
return part;
}
}).join('');
props.node.setDataValue(props.column.colId, combinedValue);
setData(prevData => ({
...prevData,
check: combinedValue,
}));
};
const handleChange = (index, event) => {
const newInputValues = [...inputValues];
newInputValues[index] = event.target.value;
setInputValues(newInputValues);
if (handleInputChange) {
handleInputChange(event);
}
};
return React.createElement(
'div',
null,
data.check.split(/(%%.*?%%)/).map((part, index) => {
if (part.match(/%%.*?%%/)) {
// If it matches the pattern, render Input element
return React.createElement(
window.dash_bootstrap_components.Input,
{
key: index,
type: props.type || 'text',
placeholder: '0,00',
setProps: () => setProps(index, inputValues[index]),
onChange: (event) => handleChange(index, event),
value: inputValues[index],
style: {
display: 'inline-block',
width: 'auto',
},
}
);
} else {
// Otherwise, render only Label element
return React.createElement(
window.dash_bootstrap_components.Label,
{
key: index,
children: part,
style: {
display: 'inline-block',
width: 'auto',
},
}
);
}
}),
);
};
Inside of the dash app you have to define ag-grid:
dag.AgGrid(
id="ag-grid",
rowData=[],
columnSize="autoSize",
dashGridOptions={
"rowHeight": 48,
"rowSelection": "multiple",
},
columnDefs=[
{
"headerName": "Check this column:",
"field": "check",
"cellRenderer": "DBC_Input_With_String",
},
],
),
Then i used a callback to populate the grid. The trick here is that you define a place where you want your input to be with this placeholder: %%INPUT%%. Javascript will look for it and render value inside %%*%%.
@callback(
[
Output("ag-grid", "rowData"),
Output("ag-grid", "defaultColDef"),
],
[
Input("interval-component", "n_intervals"),
],
)
def ag_grid_formation(n_intervals):
data = {
"check": [
"First input: %%INPUT%% Second input: %%INPUT%%",
],
}
df = pd.DataFrame(data)
defaultColDef = {
"resizable": False,
"sortable": False,
"editable": False,
}
return df.to_dict("records"), defaultColDef
Now the rowData is sorted. For data manipulation and further usage you can create another callback or use the previous one. I created a new one since I was creating a form and wanted to read the whole form once the user would click the button “SEND”. In this callback I also added some data rendering since the placeholder was written like this: %%INPUT%%.
@callback(
[
Output("dummy", "children"),
],
[Input("button-grid", "n_clicks")],
[State("ag-grid", "rowData")],
)
def ag_grid(click, data):
print(data)
updated_data_list = [
{
"check": item["check"]
.replace("%%", "")
.replace("%%INPUT%%", "No value entered"),
}
for item in data
]
print(updated_data_list)
return dash.no_update
That’s it ![]()