Пользовательская проверка перекрестных полей Angular в группе форм не работает должным образом для поля даты

#angular #datepicker #angular-material #angular-validation #angular-custom-validators

#angular #datepicker #angular-material #angular-проверка #angular-custom-validators

Вопрос:

У меня есть форма с датой начала и датой окончания. Дата начала должна быть со следующего дня, а Дата окончания должна быть не менее 7 дней с момента начала. Я использую Angular material datepicker и устанавливаю значения minDate для обоих этих полей. У меня есть пользовательский валидатор на уровне FormGroup, который проверяет логику 7 дней и отображает сообщение об ошибке для поля Даты окончания, если проверка завершается неудачно.

Проверка работает нормально, и если я вручную введу более старую дату для даты окончания, отображается сообщение об ошибке. Но если я изменю начальную дату на будущую, а затем выберу Конечную дату, чтобы проверка завершилась неудачно, сообщение об ошибке не отображается. По сути, сообщение об ошибке отображается только в том случае, если дата старше, чем изначально заданное значение minDate. При любых сбоях проверки, когда Конечная дата больше, чем значение minDate, ошибка не отображается.

Мой HTML-код —

 <form [formGroup]="dateForm" >
  <mat-form-field color="green" appearance="fill" [style.width.px]=300>
    <mat-label>Start Date</mat-label>
    <input matInput [matDatepicker]="picker2" [min]="minDateStart" [max]="maxDate" formControlName="startDate" on/>
    <mat-error>
      <p *ngIf="dateForm.controls.startDate.dirty amp;amp; dateForm.controls.startDate.errors?.required" class="alert alert-danger">This is a required field</p>
      <p *ngIf="dateForm.controls.startDate.dirty amp;amp; dateForm.controls.startDate.errors?.invalidStartDate" class="alert alert-danger">
          {{ dateForm.controls.startDate.errors?.invalidStartDate.message}} </p>
    </mat-error>
    <mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
    <mat-datepicker #picker2 color="primary"></mat-datepicker>
  </mat-form-field>
  amp;nbsp;
  <mat-form-field color="green" appearance="fill" [style.width.px]=300>
    <mat-label>End Date</mat-label>
    <input matInput [matDatepicker]="picker3" [min]="minDateEnd" [max]="maxDate" formControlName="endDate"/>
    <mat-error>
      <p *ngIf="dateForm.controls.endDate.touched amp;amp; dateForm.controls.endDate.errors?.required" class="alert alert-danger">This is a required field</p>
      <p *ngIf="dateForm.errors?.invalidEndDate" class="alert alert-danger">
        {{ dateForm.errors?.invalidEndDate.message}} </p>
    </mat-error>
    <mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>
    <mat-datepicker #picker3></mat-datepicker>
  </mat-form-field>
</form>
 

Мой TS-файл —

 const DATE_OFFSET_START = 1;
const DATE_OFFSET_END = 7;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'Date Picker Validation Issue';

  minDateStart: Date;
  minDateEnd: Date;
  maxDate: Date;
  dateForm: FormGroup

  constructor(
    private formBuilder: FormBuilder) {
  }

  ngOnInit() {
    const currentDate = new Date();
    const currentYear = currentDate.getFullYear();
    const currentDay = currentDate.getUTCDate();
    const currentMonth = currentDate.getUTCMonth();

    this.minDateEnd = new Date(currentYear, currentMonth, currentDay   DATE_OFFSET_START   DATE_OFFSET_END);
    this.minDateStart = new Date(currentYear, currentMonth, currentDay   DATE_OFFSET_START);
    this.maxDate = new Date(currentYear   999, currentMonth, currentDay);

    this.dateForm = this.formBuilder.group({
      startDate: [this.minDateStart, [Validators.required, validateStartDate]],
      endDate: [this.minDateEnd, [Validators.required]],
    }, { validators: validateEndDate('startDate', 'endDate') });
  }

}

// Validates the Form Group dates values.
// Returns error message if difference is less than specified limit.
export function validateEndDate(startDateFieldName: string, endDateFieldName: string) {
  return (fg: FormGroup) => {
    if (fg.get(endDateFieldName).value === undefined || fg.get(endDateFieldName).value == null)
      return null;
    let startDate = new Date(fg.get(startDateFieldName).value);
    let endDate = new Date(fg.get(endDateFieldName).value);

    return dateDifference(startDate, endDate) >= DATE_OFFSET_END ? null : {
      invalidEndDate: {
        message: "End Date should be atleast "   DATE_OFFSET_END   " days more than Start date!"
      }
    };
  }
}

 

validateEndDate — это пользовательский валидатор, который проверяет дату начала и окончания
{ validators: validateEndDate('startDate', 'endDate') });

И это код, который отображает ошибку —

       <p *ngIf="dateForm.controls.endDate.touched amp;amp; dateForm.controls.endDate.errors?.required" class="alert alert-danger">This is a required field</p>
      <p *ngIf="dateForm.errors?.invalidEndDate" class="alert alert-danger">
        {{ dateForm.errors?.invalidEndDate.message}} </p>
    </mat-error>
 

Я не уверен, почему ошибка отображается только тогда, когда дата меньше значения minDate, а не для всех ошибок проверки.

Ответ №1:

Я столкнулся с этой проблемой не из-за моего пользовательского средства проверки дат, а из-за угловых материальных форм. Я вижу, что вы не установили errorStateMatcher свойство в <input> теге. Когда я использовал это свойство, все работало нормально. Если вы хотите проверить, является ли родительская группа форм недопустимой, вы можете использовать errorStateMatcher свойство matInput , как описано в этом документе.

Например:

Поле DueDate с errorStateMatcher в HTML:

 <mat-form-field appearance="fill" class="full-width">
  <mat-label>Due Date</mat-label>
  <input matInput [matDatepicker]="picker"
         formControlName="dueDate"
         id="dueDate"
         [min]="minDate"
         [errorStateMatcher]="matcher">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker touchUi></mat-datepicker>
  <mat-error *ngIf="createTaskForm.errors?.dueDateGtEqRepeatUntil">
    DueDate should not be greater than or equal to Repeat Until Date.
  </mat-error>
  <mat-error *ngIf="createTaskForm.controls?.dueDate.hasError('required')">
    Due date is required.
  </mat-error>
</mat-form-field>
 

в файле TS:

 export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const invalidCtrl = control amp;amp; control.invalid;
    const invalidParent = control amp;amp; control.parent amp;amp; control.parent.invalid;
    return (invalidCtrl || invalidParent) amp;amp; (control.dirty || control.touched);
  }
}
 

Я надеюсь, что это исправит вашу проблему. Для получения других подробных сведений о перекрестной проверке между двумя полями даты это видео может быть полезно.

Комментарии:

1. Спасибо, это сработало как шарм. Я удалил amp;amp; (control.dirty || control.touched) условие, чтобы ошибка отображалась при изменении начальной даты.

2. Когда я использую его в своей существующей форме, если какое-либо из других полей в форме недопустимо, то это поле также выделялось как ошибка из-за control.parent.invalid условия. Я изменил метод для проверки наличия конкретных ошибок, чтобы решить эту проблему. const invalidCtrl = control amp;amp; control.invalid; const invalidEndDate = control amp;amp; control.parent amp;amp; control.parent.hasError('invalidEndDate'); return (invalidCtrl || invalidEndDate);