Using Third-Party Date pickers in ag-Grid

In this post I'll show you how to add third-party date pickers to ag-Grid to allow your users to easily filter and edit dates using a powerful date picker. Hopefully by the end of this blog you'll be an expert in working with date pickers in ag-Grid!

In order to illustrate this, we've created an Angular application using the PrimeNG Calendar date picker to track the amount of holidays taken. Please see the date picker illustrated in the GIF below:

Live demo

Please see the live demo below - click inside the FROM and TO column filters or cells to open the date picker. You can see the Angular demo code in Stackblitz here, and get the full github project here.


We'll be looking at:


Using the date picker for filtering

Filtering on the grid using the date picker

To use our date picker for filtering, first we have to register the component, which ag-Grid will use every time the user filters a column. You can see how to register custom date components in our documentation.

After the date component is registered, we can define our date picker:

<p-calendar
  [(ngModel)]="date"
  [panelStyleClass]="'ag-custom-component-popup'"
  [appendTo]="'body'"
  [placeholder]="'d/m/yyyy'"
  dateFormat="d/m/yy"
  [showIcon]="true"
  [monthNavigator]="true"
  [showButtonBar]="true"
  (onSelect)="onSelect($event)"
  (onClearClick)="onClearClick($event)"
  (onInput)="onInput($event)"
  ></p-calendar>
For the purposes of this blog, we won't delve into each property of the date picker, however we will be looking at the two properties appendTo and panelStyleClass which are paramount to understand when using date pickers in any framework.

💡 To learn more about the configrations for the date picker, visit the API for the component here

Configuring date picker appearance

When hosting a date picker, they are often clipped by the container. In this case, you can see below the date picker is clipped by the column menu popup.

The date picker is not attached to the document body

You solve this issue by setting the popup element to the document body (or any HTML DOM element, if you like). Here, we achieve this by simply adding [appendTo]="'body'" to the component. This allows the date picker to fully render itself.

The date picker is now attached to the body

Handling mouse clicks on the date picker

Since our date picker has a popup element outside of the parent filter, when you click on it the grid will think you clicked outside of the filter and close the column menu, as shown below:

Column menu closes when clicking on date picker

One way to get around this problem is to add the ag-custom-component-popup CSS class to the popup, which we've set by adding [panelStyleClass]="'ag-custom-component-popup'". This allows you to correctly handle clicks inside the date picker as shown below:

Now the column menu stays open when clicking on the date picker

With these changes, our date picker is now fully functional as a column filter editor.


Using the date picker for editing

Editing cells using the date picker

To enable cell editing with our date picker, first we have to register it as a cell editor component. You can see how to do that in our documentation.

Implementing the Cell Editor Component

Once the component is registered, we can start implementing the Cell Editor:

<p-calendar
  #container
  [(ngModel)]="value"
  [appendTo]="'body'"
  [style]="{ height: '100%', width: '100%' }"
  [inputStyle]="{ height: '100%', width: '100%' }"
  [monthNavigator]="true"
  yearRange="2015:2025"
  dateFormat="d/m/yy"
  (onSelect)="onSelect($event)"
  ></p-calendar>

As mentioned in the previous section, we won't be going into each of these property bindings, as they are mainly for visual purposes. It's important to note we're using [appendTo]="'body'" to ensure the popup renders correctly.

And now to implement the logic for the editor!

export class PrimeCellEditorComponent
  implements AgEditorComponent, AfterViewInit {
  params: any;
  value: Date;

  @ViewChild('container', { static: true }) public container;

  agInit(params: any): void {
    this.params = params;

    if (this.params.value) {
      const dateArray = this.params.value.split('/');

      const day = parseInt(dateArray[0]);
      const month = parseInt(dateArray[1]);
      const year = parseInt(dateArray[2]);

      this.value = new Date(year, month - 1, day);
    }
  }

  // open the calendar when grid enters edit mode, i.e. the datepicker is rendered
  ngAfterViewInit() {
    this.container.toggle();
  }

  // ensures that once a date is selected, the grid will exit edit mode and take the new date
  // otherwise, to exit edit mode after a selecting a date, click on another cell or press enter
  onSelect(event) {
    this.params.api.stopEditing(false);
  }

  getValue() {
    const d = this.value;
    return `${d.getDate()}/${d.getMonth() + 1}/${d.getFullYear()}`;
  }
}

As you can see, it is a simple component which implements agInit and getValue. These methods are required for the cell editor to work in ag-Grid. You can find out more about these methods here.


Using aggregations

💡 Learn more about aggregrations in our documentation.

We are also taking advantage of the grid's aggregation feature to compute the sum of the Pending, Approved, Completed and Total columns:

This is achieved by simply adding the aggFunc to the columns we want to aggregate by. In this case, we want to add a sum aggregation across the columns by adding aggFunc:'sum':

//  [...]
  {
    field: 'totalPending',
    aggFunc: 'sum',
    valueGetter: totalPendingValueGetter,
  },
  {
    headerName: 'APPROVED',
    field: 'totalApproved',
    aggFunc: 'sum',
    valueGetter: totalApprovedValueGetter,
  },
  {
    headerName: 'COMPLETED',
    field: 'totalCompleted',
    aggFunc: 'sum',
    valueGetter: totalCompletedValueGetter,
  },
  {
    headerName: 'TOTAL',
    field: 'total',
    aggFunc: 'sum',

    valueGetter: (params) =>
      !params.node.footer
        ? params.getValue('totalPending') +
          params.getValue('totalApproved') +
          params.getValue('totalCompleted')
        : false,
  },

Since we are not grouping, we can instead show the aggregations by adding a row at the bottom of the grid by enabling the grid property groupIncludeTotalFooter :

<ag-grid-angular
  // [...]
  [groupIncludeTotalFooter]="true">
</ag-grid-angular>

Using Cell Class Rules to style cells based on data

In order to clearly indicate the status of a request, cell style changes when the Status column data is updated. As you can see, in the following gif, the values for the status are COMPLETED, APPROVED and PENDING which have a green, orange and red colour scheme:

💡 Find out more about Cell Styling at our documentation.

This can be achieved using the Cell-Class Rules feature. We simply provide a CSS class to be applied based on a condition, as described in our documentation. See this approach illustrated below:

{
 // [...]
    field: 'status',
    cellClassRules: {
      'status-completed': function (params) {
        if (!params.node.footer) {
          return params.data.status == 'COMPLETED';
        }
      },
      'status-pending': function (params) {
        if (!params.node.footer) {
          return params.data.status == 'PENDING';
        }
      },
      'status-approved': function (params) {
        if (!params.node.footer) {
          return params.data.status == 'APPROVED';
        }
      },
    },
  },

Then in our CSS file, we add the styling for the classes:

.status-approved {
  color: #ff9800 !important;
  font-weight: bold;
}
.status-pending {
  color: #f44336 !important;
  font-weight: bold;
}
.status-completed {
  color: #4caf50 !important;
  font-weight: bold;
}

This way the cells are styled conditionally based on their value, delivering a much easier to use UI.


Summary

We hope this post has shown how to easily add third-party date pickers to the grid. Although the framework used here is Angular, the concepts remain the same and you can use other frameworks to integrate date pickers in ag-Grid. This will enable your users to filter and edit date values in ag-Grid using any of the powerful date pickers available.

If you would like to try out ag-Grid check out our getting started guides (JS / React / Angular / Vue)

Happy coding!