Angular 17 – Как добавить errorStateMatcher в массив форм, но тот, который пересекает группы форм по последовательным индексам

приведенный ниже код работает,

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
    )
  );
}
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
0
105
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, хотелось бы уточнить, что 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>/контейнер, чтобы полностью отобразить сообщение об ошибке.

Демо @ StackBlitz

.container {
  display: block;
  height: 150px;
  overflow: visible;
}

Это хорошая альтернатива. Я только что понял это обычным угловым способом. это дает детальный контроль. Прикрепите валидатор к formArray и поместите туда логику проверки (проверяя текущую и следующую formGroup). В валидаторе установите ошибки для двух соответствующих групп форм (и элементов управления внутри них). Таким образом, только соответствующие группы форм и элементы управления внутри них отображают ошибку. Ошибки прикрепляются к formGroup, чтобы обеспечить более быстрое реагирование, но прикрепление их только к элементам управления тоже работает.

Sachin 28.04.2024 16:17

@sachin, в твоем еще один вопрос у тебя есть другой способ сделать это

Eliseo 29.04.2024 10:23

Другие вопросы по теме