Angular 2 FormGroup'tan tüm doğrulama hatalarını alın


92

Bu kod verildiğinde:

this.form = this.formBuilder.group({
      email: ['', [Validators.required, EmailValidator.isValid]],
      hasAcceptedTerms: [false, Validators.pattern('true')]
    });

Sitesinden tüm doğrulama hatalarını nasıl alabilirim this.form?

Birim testleri yazıyorum ve gerçek doğrulama hatalarını iddia mesajına dahil etmek istiyorum.


Validators.pattern ('true') yerine, onay kutusunu işaretlemek için Validators.requiredTrue kullanabilirsiniz / kullanmalısınız.
Void

Yanıtlar:


146

Aynı problemle karşılaştım ve tüm doğrulama hatalarını bulmak ve bunları görüntülemek için bir sonraki yöntemi yazdım:

getFormValidationErrors() {
  Object.keys(this.productForm.controls).forEach(key => {

  const controlErrors: ValidationErrors = this.productForm.get(key).errors;
  if (controlErrors != null) {
        Object.keys(controlErrors).forEach(keyError => {
          console.log('Key control: ' + key + ', keyError: ' + keyError + ', err value: ', controlErrors[keyError]);
        });
      }
    });
  }

Form adı productFormsizinkiyle değiştirilmelidir.

Bir sonraki şekilde çalışır: tüm kontrollerimizi formdan formatta alırız {[p: string]: AbstractControl}ve hatanın ayrıntılarını almak için her hata anahtarıyla yineleriz. nullHata değerlerini atlar .

Ayrıca şablon görünümünde doğrulama hatalarını görüntülemek için değiştirilebilir console.log(..), ihtiyacınız olanla değiştirin .


2
FormArray için yukarıdaki yöntemi aynı düzende nasıl genişletebilirim?
Mohammad Sharaf Ali

Şunu mu demek istediniz ' + controlErrors[keyErrors];yerine ', controlErrors[keyErrors];?
ryanm

@ryanm hayır, baskıda nesne gibi veya dizge değeri gibi farklılıklar vardır.
Alex Efimov

ValidationErrorsaçısal 2'de nereden içe aktarabilirim ?
sainu

import { ValidationErrors } from '@angular/forms';
Craig Wayne

31

Bu, FormGroupiç desteklerle çözümdür ( burada olduğu gibi )

Test edildi: Açısal 4.3.6

get-form-validation-errors.ts

import { AbstractControl, FormGroup, ValidationErrors } from '@angular/forms';

export interface AllValidationErrors {
  control_name: string;
  error_name: string;
  error_value: any;
}

export interface FormGroupControls {
  [key: string]: AbstractControl;
}

export function getFormValidationErrors(controls: FormGroupControls): AllValidationErrors[] {
  let errors: AllValidationErrors[] = [];
  Object.keys(controls).forEach(key => {
    const control = controls[ key ];
    if (control instanceof FormGroup) {
      errors = errors.concat(getFormValidationErrors(control.controls));
    }
    const controlErrors: ValidationErrors = controls[ key ].errors;
    if (controlErrors !== null) {
      Object.keys(controlErrors).forEach(keyError => {
        errors.push({
          control_name: key,
          error_name: keyError,
          error_value: controlErrors[ keyError ]
        });
      });
    }
  });
  return errors;
}

Örnek kullanarak :

if (!this.formValid()) {
  const error: AllValidationErrors = getFormValidationErrors(this.regForm.controls).shift();
  if (error) {
    let text;
    switch (error.error_name) {
      case 'required': text = `${error.control_name} is required!`; break;
      case 'pattern': text = `${error.control_name} has wrong pattern!`; break;
      case 'email': text = `${error.control_name} has wrong email format!`; break;
      case 'minlength': text = `${error.control_name} has wrong length! Required length: ${error.error_value.requiredLength}`; break;
      case 'areEqual': text = `${error.control_name} must be equal!`; break;
      default: text = `${error.control_name}: ${error.error_name}: ${error.error_value}`;
    }
    this.error = text;
  }
  return;
}

1
Açısal 5 değişikliği - const controlErrors: ValidationErrors = form.controls [anahtar] .errors;
Kris Kilton

Doğruluğun kontrol edilmesi önerisi controlErrors ie if (controlErrors) {sadece kontrol edilmesi nullhata ise hata verecektirundefined
mtholen

8

Bu, hataları özyinelemeli olarak toplayan ve lodash(yalnızca ES6) gibi herhangi bir harici kitaplığa bağlı olmayan başka bir çeşittir:

function isFormGroup(control: AbstractControl): control is FormGroup {
  return !!(<FormGroup>control).controls;
}

function collectErrors(control: AbstractControl): any | null {
  if (isFormGroup(control)) {
    return Object.entries(control.controls)
      .reduce(
        (acc, [key, childControl]) => {
          const childErrors = collectErrors(childControl);
          if (childErrors) {
            acc = {...acc, [key]: childErrors};
          }
          return acc;
        },
        null
      );
  } else {
    return control.errors;
  }
}

6

Herhangi bir formüler yapı oluşturduktan sonra , bir Angular formdan tüm hataları geri almanın özyinelemeli yolu, formdaki tüm hataları almanın bir yolu yoktur. Bu, hata ayıklama amaçları için çok kullanışlıdır, aynı zamanda bu hataları çizmek için de yararlıdır.

Angular 9 için test edildi

getFormErrors(form: AbstractControl) {
    if (form instanceof FormControl) {
        // Return FormControl errors or null
        return form.errors ?? null;
    }
    if (form instanceof FormGroup) {
        const groupErrors = form.errors;
        // Form group can contain errors itself, in that case add'em
        const formErrors = groupErrors ? {groupErrors} : {};
        Object.keys(form.controls).forEach(key => {
            // Recursive call of the FormGroup fields
            const error = this.getFormErrors(form.get(key));
            if (error !== null) {
                // Only add error if not null
                formErrors[key] = error;
            }
        });
        // Return FormGroup errors or null
        return Object.keys(formErrors).length > 0 ? formErrors : null;
    }
}

Angular 7 kullanıyorum ve kodunuzda iki değişiklik yaptım: form.errors ?? null?? derlemesi için. Daha da önemlisi, FormGroup kontrol koşulunda, başvurumu || formParameter instanceof FormArraygerçekten açan ekledim . Teşekkürler!
Tyler Forsythe

6

Ya da derin ve dinamik formlardan bile tüm hataları almak için bu kitaplığı kullanabilirsiniz.

npm i @naologic/forms

Statik işlevi kendi formlarınızda kullanmak istiyorsanız

import {NaoFormStatic} from '@naologic/forms';
...
const errorsFlat = NaoFormStatic.getAllErrorsFlat(fg); 
console.log(errorsFlat);

Kullanmak istiyorsanız içeri NaoFromGroupaktarabilir ve kullanabilirsiniz

import {NaoFormGroup, NaoFormControl, NaoValidators} from '@naologic/forms';
...
    this.naoFormGroup = new NaoFormGroup({
      firstName: new NaoFormControl('John'),
      lastName: new NaoFormControl('Doe'),
      ssn: new NaoFormControl('000 00 0000', NaoValidators.isSSN()),
    });

   const getFormErrors = this.naoFormGroup.getAllErrors();
   console.log(getFormErrors);
   // --> {first: {ok: false, isSSN: false, actualValue: "000 00 0000"}}

Belgelerin tamamını okuyun


2

@MixerOID yanıtına dayanarak , işte bir bileşen olarak son çözümüm (belki bir kitaplık oluşturabilirim). Ayrıca FormArray'ı da destekliyorum:

import {Component, ElementRef, Input, OnInit} from '@angular/core';
import {FormArray, FormGroup, ValidationErrors} from '@angular/forms';
import {TranslateService} from '@ngx-translate/core';

interface AllValidationErrors {
  controlName: string;
  errorName: string;
  errorValue: any;
}

@Component({
  selector: 'app-form-errors',
  templateUrl: './form-errors.component.html',
  styleUrls: ['./form-errors.component.scss']
})
export class FormErrorsComponent implements OnInit {

  @Input() form: FormGroup;
  @Input() formRef: ElementRef;
  @Input() messages: Array<any>;

  private errors: AllValidationErrors[];

  constructor(
    private translateService: TranslateService
  ) {
    this.errors = [];
    this.messages = [];
  }

  ngOnInit() {
    this.form.valueChanges.subscribe(() => {
      this.errors = [];
      this.calculateErrors(this.form);
    });

    this.calculateErrors(this.form);
  }

  calculateErrors(form: FormGroup | FormArray) {
    Object.keys(form.controls).forEach(field => {
      const control = form.get(field);
      if (control instanceof FormGroup || control instanceof FormArray) {
        this.errors = this.errors.concat(this.calculateErrors(control));
        return;
      }

      const controlErrors: ValidationErrors = control.errors;
      if (controlErrors !== null) {
        Object.keys(controlErrors).forEach(keyError => {
          this.errors.push({
            controlName: field,
            errorName: keyError,
            errorValue: controlErrors[keyError]
          });
        });
      }
    });

    // This removes duplicates
    this.errors = this.errors.filter((error, index, self) => self.findIndex(t => {
      return t.controlName === error.controlName && t.errorName === error.errorName;
    }) === index);
    return this.errors;
  }

  getErrorMessage(error) {
    switch (error.errorName) {
      case 'required':
        return this.translateService.instant('mustFill') + ' ' + this.messages[error.controlName];
      default:
        return 'unknown error ' + error.errorName;
    }
  }
}

Ve HTML:

<div *ngIf="formRef.submitted">
  <div *ngFor="let error of errors" class="text-danger">
    {{getErrorMessage(error)}}
  </div>
</div>

Kullanım:

<app-form-errors [form]="languageForm"
                 [formRef]="formRef"
                 [messages]="{language: 'Language'}">
</app-form-errors>

2

Bunu deneyin, formdaki tüm kontroller için doğrulama çağıracaktır:

validateAllFormControl(formGroup: FormGroup) {         
  Object.keys(formGroup.controls).forEach(field => {  
    const control = formGroup.get(field);             
    if (control instanceof FormControl) {             
      control.markAsTouched({ onlySelf: true });
    } else if (control instanceof FormGroup) {        
      this.validateAllFormControl(control);            
    }
  });
}

1
export class GenericValidator {
    constructor(private validationMessages: { [key: string]: { [key: string]: string } }) {
    }

processMessages(container: FormGroup): { [key: string]: string } {
    const messages = {};
    for (const controlKey in container.controls) {
        if (container.controls.hasOwnProperty(controlKey)) {
            const c = container.controls[controlKey];
            if (c instanceof FormGroup) {
                const childMessages = this.processMessages(c);
                // handling formGroup errors messages
                const formGroupErrors = {};
                if (this.validationMessages[controlKey]) {
                    formGroupErrors[controlKey] = '';
                    if (c.errors) {
                        Object.keys(c.errors).map((messageKey) => {
                            if (this.validationMessages[controlKey][messageKey]) {
                                formGroupErrors[controlKey] += this.validationMessages[controlKey][messageKey] + ' ';
                            }
                        })
                    }
                }
                Object.assign(messages, childMessages, formGroupErrors);
            } else {
                // handling control fields errors messages
                if (this.validationMessages[controlKey]) {
                    messages[controlKey] = '';
                    if ((c.dirty || c.touched) && c.errors) {
                        Object.keys(c.errors).map((messageKey) => {
                            if (this.validationMessages[controlKey][messageKey]) {
                                messages[controlKey] += this.validationMessages[controlKey][messageKey] + ' ';
                            }
                        })
                    }
                }
            }
        }
    }
    return messages;
}
}

Ben onu aldı Deborahk ve onu biraz modifiye.


1
// IF not populated correctly - you could get aggregated FormGroup errors object
let getErrors = (formGroup: FormGroup, errors: any = {}) {
  Object.keys(formGroup.controls).forEach(field => {
    const control = formGroup.get(field);
    if (control instanceof FormControl) {
      errors[field] = control.errors;
    } else if (control instanceof FormGroup) {
      errors[field] = this.getErrors(control);
    }
  });
  return errors;
}

// Calling it:
let formErrors = getErrors(this.form);

0

This.form.errors özelliğini yineleyebilirsiniz.


14
Sanırım bu this.form.errorsyalnızca için doğrulama hatalarını döndürüyor this.form, için değil this.form.controls. FormGroups ve alt öğelerini (rastgele sayıda FormGroups, FormControls ve FormArrays) ayrı ayrı doğrulayabilirsiniz. Tüm hataları almak için, onları tekrar tekrar sormanız gerektiğini düşünüyorum.
Risto Välimäki

0

Büyük bir FormGroup ağacı için, lodash kullanarak ağacı temizleyebilir ve sadece hatalı kontrollerin olduğu bir ağaç elde edebilirsiniz. Bu, alt kontroller aracılığıyla tekrarlanarak (örneğin kullanılarak allErrors(formGroup)) ve tamamen geçerli tüm kontrol alt gruplarının budanması yoluyla yapılır :

private isFormGroup(control: AbstractControl): control is FormGroup {
  return !!(<FormGroup>control).controls;
}

// Returns a tree of any errors in control and children of control
allErrors(control: AbstractControl): any {
  if (this.isFormGroup(control)) {
    const childErrors = _.mapValues(control.controls, (childControl) => {
      return this.allErrors(childControl);
    });

    const pruned = _.omitBy(childErrors, _.isEmpty);
    return _.isEmpty(pruned) ? null : pruned;
  } else {
    return control.errors;
  }
}

-2

Açısal 5 kullanıyorum ve FormGroup'u kullanarak formunuzun durum özelliğini kolayca kontrol edebilirsiniz.

this.form = new FormGroup({
      firstName: new FormControl('', [Validators.required, validateName]),
      lastName: new FormControl('', [Validators.required, validateName]),
      email: new FormControl('', [Validators.required, validateEmail]),
      dob: new FormControl('', [Validators.required, validateDate])
    });

this.form.status, tüm alanlar tüm doğrulama kurallarını geçmediği sürece "GEÇERSİZ" olacaktır.

En iyi yanı, değişiklikleri gerçek zamanlı olarak algılamasıdır.


1
evet ama biz bütün bir formgroup hatalarını almak gerekir, sadece bilmek onun geçerli değil ise
Motassem MK

OP, sadece bir boole olduğu için status özelliğine dahil edilmeyen doğrulama mesajlarına ihtiyaç duyar.
Stefan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.