15

I have 2 datetime picker, endDate cannot be less than startDate

endDateAfterOrEqualValidator(formGroup): any {
    var startDateTimestamp: number;
    var endDateTimestamp: number;
    startDateTimestamp = Date.parse(formGroup.controls['startDateForm'].value);
    endDateTimestamp = Date.parse(formGroup.controls['endDateForm'].value);
    return (endDateTimestamp < startDateTimestamp) ? { endDateLessThanStartDate: true } : null;
  }

in html:

<mat-form-field>
    <input matInput  name="endDate" id="endDate" formControlName="endDateForm" [(ngModel)]="endDate" [matDatepicker]="toDatePicker"
    placeholder="To Date">
    <mat-datepicker-toggle matSuffix [for]="toDatePicker"></mat-datepicker-toggle>
    <mat-datepicker disabled="false" #toDatePicker></mat-datepicker>
    <mat-error *ngIf="trainingDetail.hasError('endDateLessThanStartDate')">Not valid<mat-error>
</mat-form-field>

with "mat-error", the message does not display. I try to change by "small"

<small *ngIf="trainingDetail.hasError('endDateLessThanStartDate')">Not valid</small>

the message well. Please advice me how to using mat-error

Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
quoc bao Pham
  • 301
  • 1
  • 5
  • 17

2 Answers2

35

a mat-error only shows when a FormControl is invalid, but you have the validation on your formgroup. So in that case you need to use a ErrorStateMatcher

In your case it would look like this:

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const invalidCtrl = !!(control && control.invalid);
    const invalidParent = !!(control && control.parent && control.parent.invalid);

    return (invalidCtrl || invalidParent);
  }
}

Also worth mentioning, it's not recommended to have two bindings, i.e formControl and ngModel. Remove the ngModel and utilize the form control instead. If you receive your start date and end date at a later point, you can use patchValue (just set some values to form) or setValue (set all values to form)

mark in component the errorstatematcher:

matcher = new MyErrorStateMatcher();

As for your custom validator, you don't need to parse the dates, just check if end date is smaller than start date:

checkDates(group: FormGroup) {
  if (group.controls.endDate.value < group.controls.startDate.value) {
    return { endDateLessThanStartDate: true }
  }
  return null;
}

and then mark the error state matcher in your template:

<mat-form-field>
  <input matInput [matDatepicker]="picker2" type="text" formControlName="endDate" [errorStateMatcher]="matcher">
  <mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
  <mat-datepicker #picker2></mat-datepicker>
  <mat-error *ngIf="myForm.hasError('endDateLessThanStartDate')">End date cannot be earlier than start date</mat-error>
</mat-form-field>

Here's a StackBlitz

AT82
  • 71,416
  • 24
  • 140
  • 167
  • Thank you so much, it's good demo. But I change the End Date to the invalid date. this error message not display – quoc bao Pham Dec 07 '17 at 05:25
  • What do you mean that you changed the end date to the invalid date? Could you demonstrate in the demo I made? – AT82 Dec 07 '17 at 06:14
  • Your default startDate in ts is 2017-11-25T22:00:00.000Z. But on UI display 11/26/2017 (maybe time zone). I try to change startDate first to 11/25/2017, and change invalid endDate. The error is displayed. Thanks – quoc bao Pham Dec 07 '17 at 06:42
  • @AJT_82 Is it possible to add "required" error state along with this custom validation? Thanks in adv. – SatAj Jan 03 '18 at 18:45
  • I have a question here please, what's the use of `form: FormGroupDirective` in the `isErrorState` method. It's not used, so we can remove it right? – Mohamad Mousheimish Jun 16 '21 at 07:03
3

If you want to set a control as invalid from the .ts file manually...

HTML:

<mat-form-field class="full-width">
  <input matInput [formControl]="exampleFormControl" (change)="changeDetected()">
  <mat-hint>(Optional)</mat-hint>
  <mat-error *ngIf="exampleFormControl.hasError('invalid')">
      Must be a <strong>valid input</strong>!
  </mat-error>
</mat-form-field>

TS:

import { FormControl } from '@angular/forms';

@Component({
  selector: 'derp',
  templateUrl: './derp.html',
  styleUrls: ['./derp.scss'],
})
export class ExampleClass {

  // Date Error Form Control
  exampleFormControl = new FormControl('');

  // Variable Check
  inputValid: boolean;

  changeDetected() {
    // Check if input valid
    if (this.inputValid) {
      console.log('Valid Input');
    } else {
      console.log('Invalid Input');
      // IMPORTANT BIT - This makes the input invalid and resets after a form change is made
      this.exampleFormControl.setErrors({
        invalid: true,
      });
    }
  }

  // CODE THAT CHANGES VALUE OF 'inputValid' //

}
About7Deaths
  • 681
  • 1
  • 5
  • 16