Editing DateTime format in ag-grid

Hi,

I’ve been working on a project recently where I utilize Ag-Grid. One of the columns that should be editable is also datetime formatted like 2023-04-01T08:00:00 Using agDateStringCellEditor worked like a charm for date editing as it opens datepicker in every edited cell.

        columnDefs = []
        for col in data.columns:
            if col == 'Date':
                columnDefs.append({
                    "field": col,
                    "editable": True,
                    "cellEditor": "agDateStringCellEditor"
                })
            else:
                columnDefs.append({"field": col})

However, I found it challenging when it came to editing both date and hour simultaneously. I’m thinking of spliting (by T) date and hour - date column would be edited with DateStringCellEditor while hour would be just a string in format hh:mm. Has anybody came across a better solution to do this?

Thank you very much,
aboh

1 Like

Here is one possible solution: using a custom date picker. I am using a datetime format of '%Y-%m-%d %H:%M:%S' in this example.

app.py

Note the external scripts and stylesheets. We will also use a custom JS function as the editor "cellEditor": {"function": "DatePicker"} rather than the agDateStringCellEditor

import dash_ag_grid as dag
from dash import Dash, html
import plotly.express as px
import pandas as pd


external_scripts = ["https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js",
                    "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js",
                    'https://trentrichardson.com/examples/timepicker/jquery-ui-timepicker-addon.js']

external_stylesheets = ['https://code.jquery.com/ui/1.13.3/themes/base/jquery-ui.css']

app = Dash(__name__, external_scripts=external_scripts, external_stylesheets=external_stylesheets)

df = px.data.medals_wide()
df['date'] = pd.date_range('2023-04-29', periods=len(df), freq='h').strftime('%Y-%m-%d %H:%M:%S')

app.layout = html.Div(
    [
        dag.AgGrid(
            id="basic-editing-example",
            columnDefs=[{"field": i} if i != 'date' else {'field': i, "cellEditor": {"function": "DatePicker"},
                                                          'cellDataType': 'dateString'}
                        for i in df.columns],
            rowData=df.to_dict("records"),
            columnSize="sizeToFit",
            defaultColDef={"editable": True, "cellDataType": False},
            dashGridOptions={"dataTypeDefinitions": {"function": "dataTypeDefinitions"}}
        ),
    ]
)

if __name__ == "__main__":
    app.run(debug=True)

dashAgGridFunctions.js

add our custom functions

var dagfuncs = (window.dashAgGridFunctions = window.dashAgGridFunctions || {});


dagfuncs.dataTypeDefinitions = {
    dateString: {
        baseDataType: 'dateString',
        extendsDataType: 'dateString',
        valueParser: (params) =>
            params.newValue != null &&
            params.newValue.match('\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}')
                ? params.newValue
                : null,
        valueFormatter: (params) => (params.value == null ? '' : params.value),
        dataTypeMatcher: (value) =>
            typeof value === 'string' && !!value.match('\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}'),
        dateParser: (value) => {
            if (value == null || value === '') {
                return undefined;
            }
            const dateParts = value.split('/');
            return dateParts.length === 3
                ? new Date(
                    parseInt(dateParts[2]),
                    parseInt(dateParts[1]) - 1,
                    parseInt(dateParts[0])
                )
                : undefined;
        },
        dateFormatter: (value) => {
            if (value == null) {
                return undefined;
            }
            const date = String(value.getDate());
            const month = String(value.getMonth() + 1);
            return `${date.length === 1 ? '0' + date : date}/${
                month.length === 1 ? '0' + month : month
            }/${value.getFullYear()}`;
        },
    },
};


dagfuncs.DatePicker = class {

    // gets called once before the renderer is used
    init(params) {
        // create the cell
        this.eInput = document.createElement('input');
        this.eInput.value = params.value;
        this.eInput.classList.add('ag-input');
        this.eInput.style.height = 'var(--ag-row-height)';
        this.eInput.style.fontSize = 'calc(var(--ag-font-size) + 1px)';

        // https://trentrichardson.com/examples/timepicker/
        $(this.eInput).datetimepicker({
            timeFormat: 'HH:mm:ss',
            dateFormat: 'yy-mm-dd'
        });
    }

    // gets called once when grid ready to insert the element
    getGui() {
        return this.eInput;
    }

    // focus and select can be done after the gui is attached
    afterGuiAttached() {
        this.eInput.focus();
        this.eInput.select();
    }

    // returns the new value after editing
    getValue() {
        return this.eInput.value;
    }

    // any cleanup we need to be done here
    destroy() {
        // but this example is simple, no cleanup, we could
        // even leave this method out as it's optional
    }

    // if true, then this editor will appear in a popup
    isPopup() {
        // and we could leave this method out also, false is the default
        return false;
    }

}

.

3 Likes

Great solution, cheers @PyGuy!