Cómo encontrar los controles no válidos en forma reactiva angular 4

89

Tengo una forma reactiva en Angular como a continuación:

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 devuelve falso, pero todo se ve bien.

Intenté encontrar comprobando la propiedad de estado en la colección de controles. Pero me pregunto si hay alguna manera de encontrar los inválidos y mostrarlos al usuario.

sa_
fuente
Si solo desea mostrar los campos con un error, puede usar css para resaltar o colorear los campos no válidos. Cada campo no válido tiene una clase "ng-invalid" agregada en su lista de clases
LookForAngular

Respuestas:

168

Simplemente puede iterar sobre cada control y verificar el estado:

public findInvalidControls() {
    const invalid = [];
    const controls = this.AddCustomerForm.controls;
    for (const name in controls) {
        if (controls[name].invalid) {
            invalid.push(name);
        }
    }
    return invalid;
}
Max Koretskyi
fuente
1
gracias por esto, pero intenté esto e incluso esto no devuelve nada, mi formulario aún no es válido, esto es extraño. Quiero decir que este código se ve bien pero no tiene ningún sentido por qué form.valid devuelve falso
sa_
que findInvalidControls()te devuelve
Max Koretskyi
1
no devuelve nada, inválido está vacío. Verifiqué uno por uno en la pantalla de depuración, todos los controles son válidos pero this.AddCustomerForm.valid todavía devuelve falso.
sa_
Creo que me enteré. hay un campo de correo electrónico y una expresión regular, pero de alguna manera el estado del control es PENDIENTE y podría ser la causa
sa_
6
@ AngularInDepth.com: si uno de los controles es un grupo de formulario, su función devolverá el grupo de formulario no válido y no el control de formulario específico que no es válido
john Smith
35

Acabo de luchar contra este problema: todos los campos de formulario son válidos, pero el formulario en sí no es válido.

Resulta que había configurado 'Validator.required' en un FormArray donde los controles se agregan / eliminan dinámicamente. Entonces, incluso si FormArray estaba vacío, todavía era necesario y, por lo tanto, el formulario siempre era inválido, incluso si todos los controles visibles se completaron correctamente.

No encontré la parte inválida del formulario, porque mi función 'findInvalidControls' solo verificó FormControl y no FormGroup / FormArray. Así que lo actualicé un poco:

/* 
   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;
  }
Jette
fuente
3
Respuesta increíblemente útil. Muchas gracias
Mikki
1
De acuerdo, respuesta muy útil.
nenea
20

En DevTools en Chrome, seleccione la pestaña Consola.

En el indicador de la consola, escriba el comando:

document.getElementsByClassName('ng-invalid')

La salida debería ser similar a esta: ingrese la descripción de la imagen aquí

En este caso, el texto subrayado es para el control de formulario listen-address. Y el texto encerrado: .ng-invalidindica que el control no es válido.

Nota: probado en cromo

Mwiza
fuente
2
me parece que esta es la forma más directa de responder a la pregunta.
ckapilla
2
Me salvaste de volverme loco, si tan solo pudiera
invitarte a
3

Tanto las formas como todos sus controles extienden la clase angular AbstractControl. Cada implementación tiene un acceso a los errores de validación.

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

Los documentos api contienen todas las referencias https://angular.io/api/forms/AbstractControl

Editar

Pensé que el acceso al error funcionaba de esta manera, sin embargo, este enlace a github muestra que hay otras personas que pensaron lo mismo que yo https://github.com/angular/angular/issues/11530

En cualquier caso, al usar el descriptor de acceso de controles, puede iterar sobre todos los controles de formulario en su formulario.

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's subcontrols too
     })
LookForAngular
fuente
1
esto devuelve nulo incluso si hay controles vacíos
sa_
1
Debería devolver un valor nulo cuando no haya errores. ¿Puedes publicar tu plantilla?
LookForAngular
Sí, esto no funcionará, las diferentes validaciones establecidas en cada control de formulario, los controles de cada formulario contienen sus errores, el formulario no. Necesita iterar los controles como Maximus ha dado respuesta.
AJT82
Puedo acceder a errores para cada contorls individuales como this.form.controls ['Email'].
Errors
@ AJT_82 de hecho, el formulario en sí puede mostrar errores si se ha configurado un validador para formGroup (verifique los documentos sobre la validación de campo cruzado, que tiene sentido validar en el grupo y no en el control)
LookForAngular
3

Ahora, en angular 9, puede usar el método markAllAsTouched () para mostrar los validadores de controles no válidos:

this.AddCustomerForm.markAllAsTouched();
Rodrigo Jorge Baptista
fuente
Darle a esto un +1, ya que me ayudó a descubrir lo que necesitaba saber, que es mostrar mensajes de validación cuando el usuario no necesariamente ha tocado las entradas.
Sean Halls
1

Si no tiene muchos campos en el formulario, simplemente puede F12 y pasar el cursor sobre el control, podrá ver la ventana emergente con los valores prístinos / tocados / válidos del campo- "# fieldname.form-control.ng- untouched.ng-invalid ".

darshna mishra
fuente
1

Creo que deberías intentar usar this.form.updateValueAndValidity()o intentar ejecutar ese mismo método en cada uno de los controles.

Javi Marzán
fuente
1

prueba esto

 findInvalidControls(f: FormGroup) {
    const invalid = [];
    const controls = f.controls;
    for (const name in controls) {
      if (controls[name].invalid) {
        invalid.push(name);
      }
    }
    return invalid;
  }
Trilok Singh
fuente
1

Esto registrará todos los nombres de los controles 😊

for (let el in this.ReactiveForm.controls) {
      if (this.ReactiveForm.controls[el].errors) {
        console.log(el)
      }
 }          

puede hacer una matriz o cadena de esto y mostrar al usuario

tejas n
fuente
0

Me tomé la libertad de mejorar el código -s de AngularInDepth.com , para que también busque de forma recursiva entradas no válidas en formularios anidados. Ya sea que esté anidado por FormArray-s o FormGroup-s. Simplemente ingrese el formGroup de nivel superior y devolverá todos los FormControls que no son válidos.

Es posible que pueda eliminar algunas de las comprobaciones de tipo "instancia de", si separa la comprobación de FormControl y la adición a la funcionalidad de matriz no válida en una función separada. Esto haría que la función se vea mucho más limpia, pero necesitaba una opción global de función única para obtener una matriz plana de todos los controles de formulario no válidos y esta es la solución.

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

Solo para aquellos que lo necesitan, para que no tengan que codificarlo ellos mismos.

Editar # 1

Se solicitó que también devuelva FormArray-s y FormGroups no válidos, por lo que si también lo necesita, use este código

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;
}
Karl Johan Vallner
fuente
1
Lo probé, pero no encuentra ningún FormGroup o FormArray no válido ... solo FormControl's inválidos. Cometí el mismo error ... mira mi respuesta.
Jette
Mejoré mi respuesta para adaptarla a su caso de uso.
Karl Johan Vallner
0

puede registrar el valor del formulario console.log(this.addCustomerForm.value), consolará el valor de todos los controles, luego los campos nulos o "" (vacíos) indican controles no válidos

Sohail Anwar
fuente