Hello all, I’m presently trying to use Dash AG-Grid for a use-case in my org. One of those is to have filters through DateTime format (“YYYY-MM-DD HH:mm”). Since, the pre-configured agDateColumnFilter doesn’t allow inputs through the aforementioned format, I’m now working on a custom script for the same. A snippet of it goes like this (I’m obscuring certain aspects of it to maintain confidentiality):
ABC.py:
# Dynamically generate columnDefs
columnDefs = []
for column in table_column_info:
col_name = column["col_name"]
data_type = column["data_type"]
# Add the column definition
column_def = {
"field": col_name,
"cellRenderer": "SpinnerCellRenderer",
"sortable": True,
"resizable": True,
"headerClass": "abcd",
"cellStyle": {"whiteSpace": "normal", "wordWrap": "break-word"},
"floatingFilter": True,
}
# Determine the filter type based on the data type
if "datetime" in data_type.lower():
column_def["filter"] = "DateTimeFilter"
elif data_type.lower() in ["string", "object"]:
column_def["filter"] = "agTextColumnFilter"
elif "int" or "float" or "double" in data_type.lower():
column_def["filter"] = "agNumberColumnFilter"
# if filter_type:
# column_def["filter"] = filter_type
columnDefs.append(column_def)
# Render the AgGrid component
return dag.AgGrid(
id="table-grid-view",
columnDefs=columnDefs,
defaultColDef={
"flex": 1,
"minWidth": 150,
"sortable": True,
"resizable": True,
},
rowModelType="infinite", # Use infinite row model
dashGridOptions={
"rowBuffer": 0,
"maxBlocksInCache": 2,
"cacheBlockSize": 100,
"cacheOverflowSize": 2,
"maxConcurrentDatasourceRequests": 2,
"infiniteInitialRowCount": 1,
"pagination": True,
"rowHeight": 30,
},
)
dashAgGridComponentFunctions.js:
var dagcomponentfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};
// Custom Date-Time Picker Filter with Menu Options
const [useImperativeHandle, useState, useEffect, forwardRef] = [
React.useImperativeHandle,
React.useState,
React.useEffect,
React.forwardRef,
];
dagcomponentfuncs.DateTimeFilter = forwardRef((props, ref) => {
const [filterType, setFilterType] = useState("equals"); // Default filter type
const [dateFrom, setDateFrom] = useState(""); // Start date
const [dateTo, setDateTo] = useState(""); // End date (for "inRange")
// Expose AG Grid Filter Lifecycle callbacks
useImperativeHandle(ref, () => ({
doesFilterPass(params) {
const cellValue = params.data[props.column.colId];
if (!cellValue) return false;
const cellDate = new Date(cellValue);
const fromDate = dateFrom ? new Date(dateFrom) : null;
const toDate = dateTo ? new Date(dateTo) : null;
switch (filterType) {
case "equals":
return fromDate && cellDate.getTime() === fromDate.getTime();
case "notEqual":
return fromDate && cellDate.getTime() !== fromDate.getTime();
case "lessThan":
return fromDate && cellDate < fromDate;
case "greaterThan":
return fromDate && cellDate > fromDate;
case "inRange":
return fromDate && toDate && cellDate >= fromDate && cellDate <= toDate;
default:
return true;
}
},
isFilterActive() {
return !!dateFrom || !!dateTo;
},
getModel() {
if (!this.isFilterActive()) {
return null;
}
return {
[props.column.colId]: { // Use the column ID as the key
type: filterType,
dateFrom: dateFrom,
dateTo: filterType === "inRange" ? dateTo : null,
filterType: "date",
},
};
},
setModel(model) {
if (model && model[props.column.colId]) {
const filterDetails = model[props.column.colId];
setFilterType(filterDetails.type || "equals");
setDateFrom(filterDetails.dateFrom || "");
setDateTo(filterDetails.dateTo || "");
} else {
setFilterType("equals");
setDateFrom("");
setDateTo("");
}
},
}));
// Notify Ag-Grid when the filter changes
useEffect(() => {
props.filterChangedCallback();
}, [filterType, dateFrom, dateTo]);
// Handle changes to the filter type
function onFilterTypeChange(event) {
setFilterType(event.target.value);
}
// Handle changes to the "dateFrom" input
function onDateFromChange(event) {
setDateFrom(event.target.value);
}
// Handle changes to the "dateTo" input (only for "inRange")
function onDateToChange(event) {
setDateTo(event.target.value);
}
return React.createElement(
"div",
{ style: { display: "flex", flexDirection: "column", padding: "5px" } },
React.createElement(
"select",
{
value: filterType,
onChange: onFilterTypeChange,
style: { marginBottom: "5px", padding: "5px", width: "100%" },
},
React.createElement("option", { value: "equals" }, "Equals"),
React.createElement("option", { value: "notEqual" }, "Does not equal"),
React.createElement("option", { value: "lessThan" }, "Before"),
React.createElement("option", { value: "greaterThan" }, "After"),
React.createElement("option", { value: "inRange" }, "Between")
),
React.createElement("input", {
type: "datetime-local", // HTML5 date-time picker
value: dateFrom,
onChange: onDateFromChange,
style: { width: "100%", padding: "5px", marginBottom: "5px" },
}),
filterType === "inRange" &&
React.createElement("input", {
type: "datetime-local", // HTML5 date-time picker
value: dateTo,
onChange: onDateToChange,
style: { width: "100%", padding: "5px" },
})
);
});
So now I’m getting the intended UI for DateTime filter. It is also getting triggered as soon as I start typing. However, I’m not able to get data from that input. For reference, I want my data to be parsed/captured like this for my filterModel
: {'XXX_YY': {'dateFrom': '2025-04-02 00:00:00', 'dateTo': None, 'filterType': 'date', 'type': 'equals'}}
where ‘XXX_YY’ is the name of column on which filter is being applied. Instead, at the moment, it just returns this: {}
.
What mistake might I be committing here? I don’t possess much working knowledge of JavaScript, so am at my wit’s end to figure out this conundrum. Or is there an easier alternative to my need - something like extending the functionality of “agDateColumnFilter” to have date-time input (instead of only date input) and the rest UI as is?
I’m using dash_ag_grid==31.2.0, plotly==5.23.0 & Python 3.12.4 and operating in Chrome browser.