приведенный ниже код работает,
https://stackblitz.com/edit/stackblitz-starters-wnququ?file=src%2Fmain.html
Но мне нужно сделать еще один шаг вперед и убедиться, что дата окончания FormGroup по индексу (x) не больше и не равна начальной дате FormGroup по индексу (x + 1) - (все внутри массива основной формы ).
Знаешь, как я это делаю?
Это то, что у меня есть на данный момент (см. также демо-версию Stackblitz)
Валидаторы
В настоящее время мой валидатор даты выглядит так:
// VALIDATORS
public startDateAfterEndDateMatcher: ValidatorFn =
this.dateComparisonValidator(
'startDate',
'endDate',
'startDateAfterEndDate',
(date1: Date, date2: Date) => date1 && date2 && date1 > date2
);
private dateComparisonValidator(
fieldName1: string,
fieldName2: string,
errorName: string,
condition: (value1: any, value2: any) => boolean
): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const field1Value = control.get(fieldName1)?.value;
const field2Value = control.get(fieldName2)?.value;
console.info('condition', condition(field1Value, field2Value));
if (condition(field1Value, field2Value)) {
const errors: ValidationErrors = {};
errors[errorName] = true;
return errors;
}
return null;
};
}
Структура формы
Структура формы на данный момент выглядит следующим образом. Валидатор формы добавляется к каждому объекту formGroup (но я бы хотел сейчас попробовать проверить все formGroups - что я не знаю, как это сделать)
private initFormGroup() {
this.datesInfo = this.formBuilder.group({
datesArray: this.formBuilder.array(
(this.datesArray || []).map((_) =>
this.formBuilder.group(
{
startDate: [
'',
{
nonNullable: true,
validators: [Validators.required],
},
],
endDate: [
'',
{
validators: [],
},
],
},
{ validators: [this.startDateAfterEndDateMatcher] }
)
)
),
});
}
Сопоставитель состояний ошибок
Мой сопоставитель состояний ошибок (который прикрепляется к каждой группе форм в массиве форм) выглядит так:
// ERROR MATCHER
export class SingleErrorStateMatcher implements ErrorStateMatcher {
private errorCode: string;
public constructor(errorCode: string, private formGroup?: FormGroup) {
this.errorCode = errorCode;
}
isErrorState(
control: FormControl | null,
formGroup: FormGroupDirective | NgForm | null
): boolean {
let parentFormGroup = this.formGroup ?? formGroup;
console.info('parentFormGroup', parentFormGroup);
return (
!!(parentFormGroup?.dirty || parentFormGroup?.touched) &&
!!(parentFormGroup?.invalid && parentFormGroup?.hasError(this.errorCode))
);
}
}
Инициализация
Они помещаются только внутрь ngOnInit (так что это не полностью динамично в том смысле, что я еще не думал о том, что произойдет, если я захочу добавить еще одну пару дат или если я удалю/откатываю пару дат... - но пока это нормально)
// create error state matchers
for (let i = 0; i < this.datesArray.length; i++) {
this.startDateAfterEndDateMatchers.push(
new SingleErrorStateMatcher(
'startDateAfterEndDate',
this.datesInfo.controls['datesArray'].get(`${i}`) as FormGroup
)
);
}





Во-первых, хотелось бы уточнить, что ErrorStateMatcher используется для определения того, как/когда отображается <mat-error>. Поэтому не следует смешивать это с логикой проверки.
Настройте SingleErrorStateMatcher, чтобы отображать ошибку, когда FormGroup недействителен вместо указанной ошибки (кода).
export class SingleErrorStateMatcher implements ErrorStateMatcher {
private errorCode: string;
public constructor(errorCode: string, private formGroup?: FormGroup) {
this.errorCode = errorCode;
}
isErrorState(
control: FormControl | null,
formGroup: FormGroupDirective | NgForm | null
): boolean {
let parentFormGroup = this.formGroup ?? formGroup;
//console.info('parentFormGroup', parentFormGroup);
return (
!!(parentFormGroup?.dirty || parentFormGroup?.touched) &&
!!parentFormGroup?.invalid
);
}
}
Для сравнения поля с последовательным (следующим) полем, думая, что подписавшись на наблюдаемые dateArrayFormArrayvaluesChanges и добавив проверку, будет проще.
subscription!: Subscription;
this.subscription = (
this.datesInfo.controls['datesArray'] as FormArray
).valueChanges.subscribe((dates) => {
dates.forEach((x: any, i: number) => {
const endDateExceedsStartDate = dates.some(
(y: any, j: number) =>
j == i + 1 && x.endDate && y.startDate && x.endDate >= y.startDate
);
const endDate = (
this.datesInfo.controls['datesArray']?.get(`${i}`) as FormGroup
)?.get('endDate')!;
if (endDateExceedsStartDate) {
endDate.setErrors(
{ endDateExceedsStartDate: true },
{ emitEvent: false }
);
} else {
if (endDate.hasError('endDateExceedsStartDate')) {
delete endDate.errors?.['endDateExceedsStartDate'];
endDate.updateValueAndValidity({ emitEvent: false });
}
}
});
});
И не забудьте отписаться от Подписки на оптимизацию производительности.
ngOnDestroy() {
this.subscription.unsubscribe();
}
Показывает ошибку «endDateExceedsStartDate».
<mat-error
*ngIf = "datesInfo.get('datesArray')!.get([$index])?.get('endDate')?.hasError('endDateExceedsStartDate')"
>
End Date cannot exceed (next) Start Date
</mat-error>
Если <mat-error> не отображается, это может быть связано с нехваткой места. Следовательно, вам может потребоваться настроить <mat-form-field>/контейнер, чтобы полностью отобразить сообщение об ошибке.
.container {
display: block;
height: 150px;
overflow: visible;
}
@sachin, в твоем еще один вопрос у тебя есть другой способ сделать это
Это хорошая альтернатива. Я только что понял это обычным угловым способом. это дает детальный контроль. Прикрепите валидатор к formArray и поместите туда логику проверки (проверяя текущую и следующую formGroup). В валидаторе установите ошибки для двух соответствующих групп форм (и элементов управления внутри них). Таким образом, только соответствующие группы форм и элементы управления внутри них отображают ошибку. Ошибки прикрепляются к formGroup, чтобы обеспечить более быстрое реагирование, но прикрепление их только к элементам управления тоже работает.