'Angular material date-picker validation

I was struggling to add validation to date-picker. This is a stackblitz link. You can see the codebase

My date-picker should just allow to DD MMM YYYY format. It should not allow DD-MM-YY, DD/MM/YYYY, and such.

I put a warning message. It works correctly. However, the date picker's own border validation does not work properly. How Can I handle this problem? The date picker should not be required.

This is inside of the typescript file.

export const MY_FORMATS = {
parse: {
 dateInput: 'DD MMM YYYY',
},
display: {
 dateInput: 'DD MMM YYYY',
 monthYearLabel: 'MMMM YYYY',
 dateA11yLabel: 'DD MM YYYY',
 monthYearA11yLabel: 'MMMM YYYY',
 },
};
@Component({
  selector: 'datepicker-overview-example',
  templateUrl: 'datepicker-overview-example.html',
  styleUrls: ['datepicker-overview-example.css'],
  providers: [
  {
   provide: DateAdapter,
   useClass: MomentDateAdapter,
   deps: [MAT_DATE_LOCALE],
  },
  { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
 ],
})

export class DatepickerOverviewExample {
 regexPattern = /^(([1-9])|([0][1-9])|([1-2][0-9])|([3][0-1]))(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\d{4}$/gi;

 date = new FormControl('', [Validators.pattern(this.regexPattern)]);

 getErrorMessage(val: string): string {
  const regexPattern = /^(([1-9])|([0][1-9])|([1-2][0-9])|([3][0-1]))(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\d{4}$/gi;
 
  const value = val.replace(/\s+/g, '');
  const isValid = regexPattern.test(value);
  console.log(isValid);
  // console.log(this.date.valid);

  if (!isValid && val !== '') {
   return 'Invalid input: Please input a string in the form of DD MMM YYYY';
  }

  return '';
 }
}

Here is the template.

<mat-form-field appearance="legacy">
 <input
  matInput
  [matDatepicker]="picker"
  placeholder="Choose a date"
  #pickerInput
  [formControl]="date"
 />
 <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
 <mat-datepicker #picker></mat-datepicker>
 <mat-error *ngIf="date.invalid">{{getErrorMessage(pickerInput.value)}}</mat-error>
</mat-form-field>

Thanks in advance!



Solution 1:[1]

I abstracted away from valid check to create a custom validator, it should work. Your pattern check doesn't check for ISO string as you might expect, I think it checked against the internal Moment Obj angular datepicker is using.

export function dateRegexValidator(
  control: AbstractControl
): { [key: string]: boolean } | null {
  const regexPattern = /^(([1-9])|([0][1-9])|([1-2][0-9])|([3][0-1]))(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\d{4}$/gi;
  let val = control.value;
  const isValid = regexPattern.test(val);

  if (isValid) {
    return { dateRegex: true };
  }
  return null;
}
<mat-form-field appearance="legacy">
  <input
    matInput
    [matDatepicker]="picker"
    placeholder="Choose a date"
    #pickerInput
    [formControl]="date"
  />
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker></mat-datepicker>
  <mat-error *ngIf="date.errors?.dateRegexValidator"
    >{{getErrorMessage(pickerInput.value)}}</mat-error
  >
</mat-form-field>

Example: https://stackblitz.com/edit/angular-b1auah-nvrb3c?file=src%2Fapp%2Fdatepicker-overview-example.ts

Solution 2:[2]

The error comes from the fact that Validators.pattern() assumes control.value is a string. However, control.value is an object.

console.log(this.date.value);
Object { _isAMomentObject: true, _i: {…}, _isUTC: false, _pf: {…}, _locale: {…}, _d: Date Wed Mar 23 2022 00:00:00 GMT-0400 (Eastern Daylight Saving Time), _isValid: true }

Whereas in your getErrorMessage() call you are getting the value of the HTMLInputElement not the FormControl.

You are using a component with built in validation and auto-format, so a validator is not necessary. You've already supplied the format so you can trust it to do it's job.

To get the formatted string you can do

  @ViewChild('pickerInput') pickerInput?: ElementRef;
  get formattedDate(): string | undefined {
    return this.pickerInput?.nativeElement.value;
  }

Or convert the object in this.date.value manually. You can always double check that the string is valid before submitting, which would indicate the date picker is broken.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Joosep Parts
Solution 2