angularangular-material-7

How do I set Min and Max values on a Datepicker field based on two other Datepicker fields - Angular Material


I have 3 date picker fields, contractPeriodFrom, contractPeriodTo and dateOfAppointment. The dateOfAppointment should be between the contractPeriodFrom and contractPeriodTo. I am unsure how to do this as both contractPeriod fields are form controls.

        <div fxLayout="row">
            <mat-form-field fxFlex="50">
                <input matInput [matDatepicker]="picker2" formControlName="contractPeriodFrom" readonly>
                <mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
                <mat-datepicker #picker2></mat-datepicker> 
            </mat-form-field>
            <mat-form-field fxFlex="50">
                <input matInput [matDatepicker]="picker3" formControlName="contractPeriodTo" readonly>
                <mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>
                <mat-datepicker #picker3></mat-datepicker> 
            </mat-form-field>
        </div>
        <div fxLayout="row" class="mt-16">
            <mat-form-field fxFlex="50">
                <input matInput [matDatepicker]="picker1" formControlName="dateOfAppointment" readonly>
                <mat-datepicker-toggle matSuffix [for]="picker1"></mat-datepicker-toggle>
                <mat-datepicker #picker1></mat-datepicker> 
            </mat-form-field>
        </div>

The form fields are part of a form group and initialized as below -

    this.lieUpdateForm = this._formBuilder.group({
        dateOfAppointment: [this.selectedLIE.dateOfAppointment || ''],
        contractPeriodFrom: [this.selectedLIE.contractPeriodFrom || ''],
        contractPeriodTo: [this.selectedLIE.contractPeriodTo || ''],
    }); 

Solution

  • Have a look here https://material.angular.io/components/datepicker/overview#date-validation. There are two solutions min/max or matDatepickerFilter. I would suggest you to use the matDatepickerFilter one, being cleaner , more flexible and not requiring subscriptions to valueChanges.

    matDatepickerFilter

    Add the [matDatepickerFilter] binding to the input:

    <input matInput
           [matDatepicker]="picker1"
           formControlName="dateOfAppointment" readonly
           [matDatepickerFilter]="dateFilter"
    >
    

    Then add a validation function to your component (note that is a lambda and not a member function, read more here MatDatepickerFilter - Filter function can't access class variable)

    public dateFilter = (d: Date): boolean => {
      const value = this.lieUpdateForm.value;
      return (d >= this.toDate(value.contractPeriodFrom)) && (d <= this.toDate(value.contractPeriodTo));
    }
    

    min/max

    Add the [min] and [max] binding to the input:

    <input matInput
           [matDatepicker]="picker1"
           formControlName="dateOfAppointment" readonly
           [min]="minDate" [max]="maxDate"
    >
    

    Then after right after assigning lieUpdateForm add:

    this._subscription = this.lieUpdateForm
      .valueChanges.subscribe(value => {
      this.minDate = toDate(value.contractPeriodFrom);
      this.maxDate = toDate(value.contractPeriodTo);
    });
    

    Do not forget to clear _subscription onDestroy

    toDate

    toDate is a function that makes sure we are dealing with Date objects. According to the documentation it should work just fine without it. I added it just in case. This is the implementation if needed:

    protected toDate(d: Date | string): Date {
      return (typeof d === 'string') ? new Date(d) : d;
    }