Dapatkan semua kesalahan validasi dari Angular 2 FormGroup


91

Diberikan kode ini:

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

Bagaimana saya bisa mendapatkan semua kesalahan validasi this.form?

Saya sedang menulis pengujian unit dan ingin memasukkan kesalahan validasi aktual dalam pesan assert.


Alih-alih Validators.pattern ('true') Anda bisa / harus menggunakan Validators.requiredTrue untuk memaksa kotak centang yang dicentang.
Void

Jawaban:


142

Saya menemui masalah yang sama dan untuk menemukan semua kesalahan validasi dan menampilkannya, saya menulis metode selanjutnya:

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

Nama formulir productFormharus diubah menjadi milik Anda.

Ini bekerja dengan cara berikutnya: kita mendapatkan semua kontrol kita dari formulir dalam format {[p: string]: AbstractControl}dan diulangi oleh setiap kunci kesalahan, untuk mendapatkan detail kesalahan. Ini melewatkan nullnilai kesalahan.

Ini juga dapat diubah untuk menampilkan kesalahan validasi pada tampilan templat, cukup ganti console.log(..)dengan yang Anda butuhkan.


2
Bagaimana cara memperluas metode di atas untuk FormArray dalam pola yang sama?
Mohammad Sharaf Ali

Apakah yang Anda maksud ' + controlErrors[keyErrors];bukan ', controlErrors[keyErrors];?
ryanm

@ryanm no, ada perbedaan di printing like object, atau like string value.
Alex Efimov

dari mana saya bisa mengimpor ValidationErrorsdi angular 2?
sainu

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

31

Ini adalah solusi dengan FormGroupdukungan dari dalam ( seperti di sini )

Dites pada: Angular 4.3.6

get-form-validation-error.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;
}

Menggunakan contoh :

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

Saran untuk memeriksa kebenarannya controlErrors yaitu if (controlErrors) {karena hanya memeriksa nullakan memberikan kesalahan jika ada kesalahanundefined
mtholen

8

Ini adalah varian lain yang mengumpulkan kesalahan secara rekursif dan tidak bergantung pada pustaka eksternal seperti lodash(hanya 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

Cara rekursif untuk mengambil semua kesalahan dari bentuk Angular , setelah membuat segala jenis struktur formularium, tidak ada cara untuk mengambil semua kesalahan dari formulir. Ini sangat berguna untuk tujuan debugging tetapi juga untuk merencanakan kesalahan tersebut.

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

Saya menggunakan Angular 7 dan membuat dua modifikasi pada kode Anda: form.errors ?? nullSaya harus menghapus ?? untuk dikompilasi. Lebih penting lagi, dalam kondisi cek FormGroup, saya menambahkan || formParameter instanceof FormArrayyang benar-benar membuka aplikasi saya. Terima kasih!
Tyler Forsythe

6

Atau Anda bisa menggunakan pustaka ini untuk mendapatkan semua kesalahan, bahkan dari bentuk yang dalam dan dinamis.

npm i @naologic/forms

Jika Anda ingin menggunakan fungsi statis pada formulir Anda sendiri

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

Jika Anda ingin menggunakan NaoFromGroupAnda dapat mengimpor dan menggunakannya

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

Baca dokumentasi lengkapnya


2

Berdasarkan tanggapan @MixerOID , berikut adalah solusi terakhir saya sebagai komponen (mungkin saya membuat perpustakaan). Saya juga mendukung 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;
    }
  }
}

Dan HTML:

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

Pemakaian:

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

2

Coba Ini, ini akan memanggil validasi untuk semua kontrol dalam bentuk:

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

Saya mengambilnya dari Deborahk dan mengubahnya sedikit.


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

Anda dapat mengulang properti this.form.errors.


14
Saya rasa itu this.form.errorshanya mengembalikan kesalahan validasi untuk this.form, bukan untuk this.form.controls. Anda dapat memvalidasi FormGroups dan turunannya (sembarang jumlah FormGroups, FormControls dan FormArrays) secara terpisah. Untuk mengambil semua kesalahan, saya pikir Anda perlu menanyakannya secara rekursif.
Risto Välimäki

0

Untuk pohon FormGroup yang besar, Anda dapat menggunakan lodash untuk membersihkan pohon dan mendapatkan pohon hanya dari kontrol dengan kesalahan. Ini dilakukan dengan mengulang melalui kontrol anak (misalnya menggunakan allErrors(formGroup)), dan memangkas subkelompok kontrol yang sepenuhnya valid:

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

Saya menggunakan angular 5 dan Anda cukup memeriksa properti status formulir Anda menggunakan mis. 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 akan menjadi "INVALID" kecuali semua kolom lolos semua aturan validasi.

Bagian terbaiknya adalah mendeteksi perubahan secara real-time.


1
ya tapi kita perlu mendapatkan kesalahan dari seluruh formgroup, bukan hanya tahu apakah itu tidak valid
Motassem MK

OP membutuhkan pesan validasi, yang tidak termasuk dalam properti status, karena ini hanya boolean.
Stefan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.