Angular 2 FormGroup에서 모든 유효성 검사 오류 가져 오기


90

이 코드가 주어지면 :

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

모든 유효성 검사 오류를 this.form어떻게 얻을 수 있습니까?

단위 테스트를 작성 중이며 어설 션 메시지에 실제 유효성 검사 오류를 포함하고 싶습니다.


Validators.pattern ( 'true') 대신 Validators.requiredTrue를 사용하여 확인란을 선택하도록 할 수 있습니다.
Void

답변:


141

나는 같은 문제를 만났고 모든 유효성 검사 오류를 찾아서 표시하기 위해 다음 방법을 작성했습니다.

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]);
        });
      }
    });
  }

양식 이름 productForm은 귀하의 것으로 변경해야합니다.

다음 방식으로 작동합니다. 형식의 모든 컨트롤을 형식으로 가져 {[p: string]: AbstractControl}오고 오류에 대한 세부 정보를 얻기 위해 각 오류 키로 반복합니다. null오류 값을 건너 뜁니다 .

또한 템플릿보기에 유효성 검사 오류를 표시하기 위해 변경할 수 있으며 console.log(..)필요한 항목으로 교체 하면됩니다.


2
위의 FormArray 메서드를 동일한 패턴으로 확장하는 방법은 무엇입니까?
Mohammad Sharaf Ali

당신을 찾으시는 것 ' + controlErrors[keyErrors];대신에 ', controlErrors[keyErrors];?
ryanm

@ryanm 아니, 객체와 같은 인쇄 또는 문자열 값과는 다릅니다.
Alex Efimov

ValidationErrors각도 2에서 가져올 수있는 곳은 ?
sainu

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

31

이것은 FormGroup내부 지지대 가있는 솔루션입니다 ( 여기처럼 )

테스트 대상 : Angular 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;
}

사용 예 :

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
Angular 5 변경-const controlErrors : ValidationErrors = form.controls [key] .errors;
Kris Kilton

controlErrors 즉 , 오류가있는 경우 if (controlErrors) {에만 확인 null하면 오류가 발생 하므로 진실을 확인하라는 제안undefined
mtholen

8

이것은 오류를 재귀 적으로 수집하고 다음과 같은 외부 라이브러리에 의존하지 않는 또 다른 변형입니다 lodash(ES6 만 해당).

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

Angular 양식에서 모든 오류를 검색하는 재귀적인 방법은 어떤 종류의 공식 구조를 만든 후에 양식 에서 모든 오류를 검색 할 수있는 방법이 없습니다. 이는 디버깅 목적뿐만 아니라 이러한 오류를 표시하는 데에도 매우 유용합니다.

Angular 9 용으로 테스트 됨

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을 사용하고 있으며 코드를 두 가지 수정 form.errors ?? null했습니다. 컴파일합니다. 더 중요한 것은 FormGroup 검사 조건에서 || formParameter instanceof FormArray실제로 내 응용 프로그램을 열었습니다. 감사!
Tyler Forsythe

6

또는이 라이브러리를 사용하여 깊고 동적 인 양식에서도 모든 오류를 가져올 수 있습니다.

npm i @naologic/forms

자신의 양식에서 정적 함수를 사용하려는 경우

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

사용 NaoFromGroup하려면 가져 와서 사용할 수 있습니다.

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"}}

전체 문서 읽기


2

@MixerOID 응답을 기반으로 , 여기에 구성 요소로서의 최종 솔루션이 있습니다 (아마도 라이브러리를 만들 수도 있습니다). 또한 FormArray를 지원합니다.

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;
    }
  }
}

그리고 HTML :

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

용법:

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

2

이것을 시도하면 형식의 모든 컨트롤에 대한 유효성 검사를 호출합니다.

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;
}
}

Deborahk 에서 가져 와서 약간 수정했습니다.


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 속성을 반복 할 수 있습니다.


14
나는 그 생각 this.form.errors반환에 대한 검증의 오류 만 this.form하지를 들어, this.form.controls. FormGroups 및 해당 자식 (임의의 FormGroups, FormControls 및 FormArrays)을 개별적으로 유효성을 검사 할 수 있습니다. 모든 오류를 가져 오려면 재귀 적으로 질문해야한다고 생각합니다.
Risto Välimäki

0

큰 FormGroup 트리의 경우 lodash를 사용하여 트리를 정리하고 오류가있는 컨트롤의 트리 만 가져올 수 있습니다. 이는 하위 컨트롤 (예 : 사용 allErrors(formGroup))을 통해 반복 하고 완전히 유효한 하위 컨트롤 그룹을 정리 하여 수행됩니다 .

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

나는 각도 5를 사용하고 있으며 FormGroup을 사용하여 양식의 상태 속성을 간단히 확인할 수 있습니다.

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는 모든 필드가 모든 유효성 검사 규칙을 통과하지 않으면 "INVALID"가됩니다.

가장 중요한 부분은 실시간으로 변화를 감지한다는 것입니다.


1
네하지만 우리는 전체 formgroup의 오류를 얻을 필요가 아닌 알고는 유효하지 않은 경우
Motassem MK

OP에는 단지 부울이기 때문에 상태 속성에 포함되지 않은 유효성 검사 메시지가 필요합니다.
Stefan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.