Как найти недействительные элементы управления в angular 4 реактивной форме

У меня есть реактивная форма в Angular, как показано ниже:

this.AddCustomerForm = this.formBuilder.group({
    Firstname: ['', Validators.required],
    Lastname: ['', Validators.required],
    Email: ['', Validators.required, Validators.pattern(this.EMAIL_REGEX)],
    Picture: [''],
    Username: ['', Validators.required],
    Password: ['', Validators.required],
    Address: ['', Validators.required],
    Postcode: ['', Validators.required],
    City: ['', Validators.required],
    Country: ['', Validators.required]
});

createCustomer(currentCustomer: Customer) 
{
    if (!this.AddCustomerForm.valid)
    {
        //some app logic
    }
}

this.AddCustomerForm.valid возвращает false, но все выглядит хорошо.

Я попытался найти с проверкой свойства статуса в коллекции элементов управления. Но мне интересно, есть ли способ найти недействительные и показать пользователю?

Ответ 1

Вы можете просто перебрать все элементы управления и проверить статус:

public findInvalidControls() {
    const invalid = [];
    const controls = this.AddCustomerForm.controls;
    for (const name in controls) {
        if (controls[name].invalid) {
            invalid.push(name);
        }
    }
    return invalid;
}

Ответ 2

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

Оказывается, я установил Validator.required в FormArray, где элементы управления добавляются/удаляются динамически. Таким образом, даже если FormArray был пустым, он все еще требовался, и поэтому форма всегда была недействительной, даже если каждый видимый элемент управления был правильно заполнен.

Я не нашел недопустимую часть формы, потому что моя функция 'findInvalidControls' проверяла только FormControl, а не FormGroup/FormArray. Поэтому я немного его обновил:

/* 
   Returns an array of invalid control/group names, or a zero-length array if 
   no invalid controls/groups where found 
*/
public findInvalidControlsRecursive(formToInvestigate:FormGroup|FormArray):string[] {
    var invalidControls:string[] = [];
    let recursiveFunc = (form:FormGroup|FormArray) => {
      Object.keys(form.controls).forEach(field => { 
        const control = form.get(field);
        if (control.invalid) invalidControls.push(field);
        if (control instanceof FormGroup) {
          recursiveFunc(control);
        } else if (control instanceof FormArray) {
          recursiveFunc(control);
        }        
      });
    }
    recursiveFunc(formToInvestigate);
    return invalidControls;
  }

Ответ 3

Обе формы и все ваши элементы управления расширяют класс angular AbstractControl. Каждая реализация имеет аксессуар ошибок проверки.

let errors = this.AddCustomerForm.errors
// errors is an instance of ValidatorErrors

Api docs содержит все ссылки https://angular.io/api/forms/AbstractControl

Edit

Я думал, что обработчик ошибок работал таким образом, но эта ссылка на github показывает, что есть другие люди, которые думали так же, как я https://github.com/angular/angular/issues/11530

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

Object.keys(this.AddCustomerForm.controls)
    .forEach( control => {
        //check each control here
        // if the child is a formGroup or a formArray
        // you may cast it and check it subcontrols too
     })

Ответ 4

Если у вас не много полей в форме, вы можете просто F12 и навести курсор на элемент управления, вы сможете увидеть всплывающее окно с полем нетронутым /touch/valid values- "# fieldname.form-control.ng -untouched.ng-инвалид".

Ответ 5

Я позволил себе усовершенствовать код AngularInDepth.com -s, чтобы он также рекурсивно искал недопустимые входные данные во вложенных формах. Вложен ли он в FormArray -s или FormGroup -s. Просто введите formGroup верхнего уровня, и он вернет все недействительные элементы FormControl.

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

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] {
    if ( ! _invalidControls ) _invalidControls = [];
    if ( _input instanceof FormControl  ) {
        if ( _input.invalid ) _invalidControls.push( _input );
        return _invalidControls;
    }

    if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;

    const controls = _input.controls;
    for (const name in controls) {
        let control = controls[name];
        switch( control.constructor.name )
        {
            case 'AbstractControl':
            case 'FormControl':
                if (control.invalid) _invalidControls.push( control );
                break;

            case 'FormArray':
                (<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
                break;

            case 'FormGroup':
                _invalidControls = findInvalidControls( control, _invalidControls );
                break;
        }
    }

    return _invalidControls;
}

Просто для тех, кому это нужно, чтобы им не приходилось кодировать это самим..

Редактировать # 1

Было запрошено, что он также возвращает недопустимые FormArray -s и FormGroups, поэтому, если вам это также нужно, используйте этот код

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] {
    if ( ! _invalidControls ) _invalidControls = [];
    if ( _input instanceof FormControl  ) {
        if ( _input.invalid ) _invalidControls.push( _input );
        return _invalidControls;
    }

    if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;

    const controls = _input.controls;
    for (const name in controls) {
        let control = controls[name];
        if (control.invalid) _invalidControls.push( control );
        switch( control.constructor.name )
        {    
            case 'FormArray':
                (<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
                break;

            case 'FormGroup':
                _invalidControls = findInvalidControls( control, _invalidControls );
                break;
        }
    }

    return _invalidControls;
}

Ответ 6

Вы можете console.log(this.addCustomerForm.value) значение формы console.log(this.addCustomerForm.value), оно будет поддерживать все значения элемента управления, а затем пустые или пустые поля указывают на недопустимые элементы управления.