Как использовать встроенный валидатор Angular в настраиваемом ControlValueAccessor

У меня есть настраиваемый компонент, наследующий ControlValueAccessor в моем приложении, который интегрирует ng-select

Цель этого компонента - иметь одно центральное место, где интегрирован ng-select, чтобы я мог повторно использовать его во всем приложении.

Вот как я называю метод проверки:

  constructor(
    @Optional() @Self() public controlDir: NgControl
  ) {
    controlDir.valueAccessor = this;
  }

  ngOnInit() {
    const control = this.controlDir.control;
    if (control.validator) {
      control.setValidators([this.validate.bind(this, [control.validator])]);
    }
    control.updateValueAndValidity({ emitEvent: false });
  }

  validate(validators: ValidatorFn[], c: FormControl) {
    const allErrors: any = {};
    for (const validator of validators) {
      const hasError = validator(c);
      if (hasError) {
        Object.entries(hasError).forEach(([errorName, isOnError]) => {
          allErrors[errorName] = isOnError;
        });
      }
    }

    if (Object.keys(allErrors).length > 0) {
      return allErrors;
    }

    return null;
  }

И вот как я обычно создаю экземпляр formControl в родительском компоненте:

const control = this.formBuilder.control(['[email protected]'], [Validators.email]);

this.form = this.formBuilder.group({ test: control });

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

Проблема в том, что контрольное значение моего пользовательского компонента представляет собой массив строк, например, значение будет:

[ '[email protected]', '[email protected]' ]

компонент выглядит так

снимок экрана компонента

Проблема: в моей функции проверки у меня есть доступ к ValidatorFn, который проверяет, является ли мой элемент управления действительным адресом электронной почты. если бы моим контрольным значением была строка, она работала бы так, как ожидалось, но это массив строк, поэтому он не работает.

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

Но я не могу понять, как определить, что валидатор определен как Validator.email.

this.controlDir.control.validator - это функция, и я понятия не имею, как определить, что это валидатор электронной почты, чтобы я мог добавить свою собственную проверку для электронной почты.

Вопрос: как я могу узнать из моей пользовательской функции проверки, какой валидатор был установлен от родителя? Это Validators.required, Validators.email ... и т. Д.


person Tonio    schedule 08.10.2020    source источник


Ответы (2)


Как я могу узнать из моей пользовательской функции проверки, какой валидатор был установлен от родителя?

К сожалению, нет способа получить валидаторы для данного элемента управления (подробности).

Теоретически вы можете перебрать свое контрольное значение (если это массив) и создать новый FormControl для проверки каждой строки в массиве.

Например, это можно сделать так:

isControlValid(controlValue: string[], validator: ValidatorFn) {
  let emailHasError = false;
  for (value of controlValue) {
    const ctrl = new FormControl(value, validator);
    if (Object.keys(validator(ctrl)).length) {
      emailHasError = true; // if any of the values are invalid
    }
  }
  return emailHasError; // or return whatever you need to.
}

Используйте это, возможно, так

validate(validators: ValidatorFn[], c: FormControl) {
  const allErrors: any = {};
  for (const validator of validators) {
    if (Array.isArray(c.value)) {
      if (this.isControlValid(c.value, validator)) {
        // maybe update `allErrors` here or something

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

Возможно, есть какой-то неясный способ добиться этого по-другому, используя NG_VALIDATORS, но я не изучал его.

person Michael Doye    schedule 16.10.2020
comment
Спасибо за ссылку на проблему с Angular, немного безумие, что эта проблема открывалась так долго. Ваш обходной путь работает отлично, он выполняет свою работу за меня. Спасибо - person Tonio; 19.10.2020

Как я могу узнать из моей пользовательской функции проверки, какой валидатор был установлен от родителя? Это Validators.required, Validators.email

Каждая функция валидатора возвращает объект ошибки. Если вы видите Validators.email () от angular, он возвращает { 'email': true } объект. Итак, если вы перебрали control.errors (или hasError в вашем примере), вы можете проверить, совпадает ли ключ для любого объекта с email -

static email(control) {
        if (isEmptyInputValue(control.value)) {
            return null; // don't validate empty values to allow optional controls
        }
        return EMAIL_REGEXP.test(control.value) ? null : { 'email': true };
    }

Вот как будет выглядеть ваша validate() функция -

validate(validators: ValidatorFn[], c: FormControl) {
    const allErrors: any = {};
    for (const validator of validators) {
      const hasError = validator(c);
      if (hasError) {
        Object.entries(hasError).forEach(([errorName, isOnError]) => {
          if(errorName === 'email' ) {
            console.log('Validators.email was set');
          }
          allErrors[errorName] = isOnError;
        });
      }
    }

    if (Object.keys(allErrors).length > 0) {
      return allErrors;
    }

    return null;
  }
person Akash    schedule 14.10.2020
comment
Проблема в этой строке: const hasError = validator (c); Из-за моей индивидуальной реализации моего formControl это не работает. Это не работает, потому что formControl представляет собой массив строк. Вот почему я хочу знать, что является валидатором перед этой строкой, чтобы иметь возможность применять настраиваемую логику, чтобы проверить, действителен ли formControl или нет. - person Tonio; 15.10.2020
comment
У вас есть собственный валидатор электронной почты? Не могли бы вы показать это? Если встроенные валидаторы angular работают с вашим пользовательским компонентом, когда элемент управления электронной почтой не является массивом, ваш пользовательский валидатор электронной почты также должен работать (если он реализован правильно). Можете ли вы воспроизвести вашу проблему на StackBlitz.com? Это позволяет другим очень легко понять, чего вы пытаетесь достичь. - person Akash; 15.10.2020
comment
Я знаю, что мне просто нужно воспроизвести на stackblitz гораздо больше работы. Я сделаю это на следующей неделе. У меня еще нет настраиваемых валидаторов электронной почты. проблема не в самом валидаторе, проблема в следующем: как узнать, что родительский компонент использует валидаторы. отправьте электронное письмо, что сборка в валидаторе не работает с моей структурой данных (потому что она настраиваемая - это имеет смысл), так что я хочу is: Если родитель использует Validators.email = ›реализовать мою собственную логику. Я просто не знаю, как узнать, какой валидатор был установлен от Родителя. - person Tonio; 15.10.2020
comment
Вы пробовали использовать if(validator === Validators.email){} до const hasError = validator(c)? - person Akash; 15.10.2020
comment
Я сделал это, и это не работает, validator - это функция, которая принимает formControl в качестве аргумента. Хотя это была хорошая попытка. :-) - person Tonio; 15.10.2020