Search code examples
angulartypescriptprimeng

Flexible date filter in PrimeNG


I am creating a flexible filter based on a datePicker that allows users to select a specific date, a range, a month, or a year. This is my current work in progress. While it functions to some extent, I am not satisfied with its appearance.

Go to Updated section, skip this first attempt

My goal is to display a single input showing the current value, similar to other filters (for example, the amount column in the provided image). I attempted to add an input manually because, without it, only a button with a calendar icon is shown. However, the standalone input field appears different. Although I could invest time in CSS adjustments, I don't find that approach ideal.

The default input filter is displayed within the popover, meaning I end up with two inputs. Is there a way to maintain a standard filter look and feel while customizing the picker (for example, to allow switching between various input types)?

I believe that having multiple instances of the datePicker is not optimal—instead, I should have a single element that I customize on the fly. This issue stems from using tabs. Is there a better method to handle this switching functionality?

enter image description here

Here is a demo: https://stackblitz.com/edit/literakl-primeng-flexible-date-filter?file=src%2Fapp%2Fgrid%2Fgrid.component.html

<p-columnFilter field="settledAt" type="date" [matchModeOptions]="dateMatchModeOptions" [showClearButton]="false">
  <ng-template pTemplate="filter" let-value let-filter="filterCallback">
    <div class="datepicker-filter">
      <input [(ngModel)]="displayDate" (click)="popover.toggle($event)" placeholder="Select date" readonly>
      <p-popover #popover [dismissable]="true">
        <p-tabView>
          <p-tabPanel header="Single Day">
            <p-datePicker [(ngModel)]="singleDate" dateFormat="mm/dd/yy" appendTo="body" (ngModelChange)="onSingleDateChange(filter, singleDate)"></p-datePicker>
          </p-tabPanel>
          <p-tabPanel header="Date Range">
            <p-datePicker [(ngModel)]="rangeDates" selectionMode="range" dateFormat="mm/dd/yy" appendTo="body" (ngModelChange)="onDateRangeChange(filter, rangeDates)"></p-datePicker>
          </p-tabPanel>
          <p-tabPanel header="Month">
            <p-datePicker [(ngModel)]="monthDate" view="month" dateFormat="MM yy" appendTo="body" (ngModelChange)="onMonthChange(filter, monthDate)"></p-datePicker>
          </p-tabPanel>
          <p-tabPanel header="Year">
            <p-datePicker [(ngModel)]="yearDate" view="year" dateFormat="yy" appendTo="body" (ngModelChange)="onYearChange(filter, yearDate)"></p-datePicker>
          </p-tabPanel>
        </p-tabView>
      </p-popover>
    </div>
  </ng-template>
</p-columnFilter>

Update

Though this solution was working, the UX was bad, because of duplicated input field and neccessity to click twice to open the date picker. I realized the date picker has a head template where I can put buttons. I was able to implement it and it looked fine.

The trouble was when I switched from Day mode to other modes like Month or Year. Selecting month worked fine, but when I closed the date picker and reopened it, the date picker width was reduced, all padding was gone. When switching back to Date, two overlapping boxes were displayed. When I selected a sigle date, closed the dialog and reopened it, it just started to work again, until I selected a period.

StackBlitz demonstrates the issue but not so dramatically like in a real project where I have many columns.

https://stackblitz.com/edit/literakl-primeng-flexible-date-filter-v2?file=src%2Fapp%2Fgrid%2Fgrid.component.html,src%2Fapp%2Fgrid%2Fgrid.component.ts

enter image description here enter image description here enter image description here enter image description here

<p-datePicker
  [(ngModel)]="singleDate"
  (ngModelChange)="onSingleDateChange(filter, singleDate)"
  (onFocus)="onTimePickerFocus($event)"
  [dateFormat]="getTimePickerFormat()"
  [view]="getTimePickerMode()"
  [selectionMode]="getTimePickerSelectionMode()"
  showClear="true"
  [readonlyInput]="true"
  [showButtonBar]="true"
  [showIcon]="true"
  [showClear]="false"
  appendTo="body"
  [fluid]="true"
>
  <ng-template pTemplate="header">
    <p-selectButton
      [options]="dateTypeOptions"
      [(ngModel)]="selectedDateType"
      (onChange)="onDateTypeChange($event.value)"
    >
    </p-selectButton>
  </ng-template>
</p-datePicker>

How to fix it please?


Solution

  • Use panelStyle prop to adjust the width of the datepicker container. If you set the width to min-content then there will not be any overflowing content in the datepicker container. So switching between the Date and Month selection will not alter the width of the datepicker.

    [panelStyle]="{ width: 'min-content' }"
    

    Checkout this working Stackblitz demo.

    Full code:

    <p-datePicker
      [(ngModel)]="singleDate"
      (ngModelChange)="onSingleDateChange(filter, singleDate)"
      (onFocus)="onTimePickerFocus($event)"
      [dateFormat]="getTimePickerFormat()"
      [view]="getTimePickerMode()"
      [selectionMode]="getTimePickerSelectionMode()"
      showClear="true"
      [readonlyInput]="true"
      [showButtonBar]="true"
      [showIcon]="true"
      [showClear]="false"
      appendTo="body"
      [fluid]="true"
      [panelStyle]="{ width: 'min-content' }"
    >
      <ng-template pTemplate="header">
        <p-selectButton
          [options]="dateTypeOptions"
          [(ngModel)]="selectedDateType"
          (onChange)="onDateTypeChange($event.value)"
        >
        </p-selectButton>
      </ng-template>
    </p-datePicker>