Los formularios reactivos Angular 2 activan la validación al enviar


¿Hay alguna manera de que todos los validadores de formularios reactivos puedan activarse al enviar y no solo por los eventos "sucio" y "toque"?

La razón de esto es que tenemos formularios muy grandes que no indican si un campo es requerido o no, y el usuario podría perder parte del control requerido, por lo que al enviar, se espera que se muestren todos los campos no válidos que se pierden por el usuario final.

He intentado marcar el formulario como "tocado" usando el

FormGroup.markAsTouched(true);

Funcionó, así que también intenté marcarlo como "sucio"

FormGroup.markAsDirty(true);

Pero el css de la clase sigue siendo "ng-pristine",

¿Hay alguna manera de activarlo manualmente desde el componente, traté de googlear sin éxito, gracias de antemano!

ACTUALIZACIÓN

Ya lo tengo funcionando iterando el FormGroup.controla y marcarlo como" sucio", pero hay una forma" estándar " de hacer esto.

Author: arvstracthoughts, 2016-10-06

6 answers

Esto se puede lograr con la muestra presentada aquí, donde se puede hacer uso de NgForm directiva:

<form [formGroup]="heroForm" #formDir="ngForm">

Y luego en sus mensajes de validación simplemente verifique si se envía el formulario:

<small *ngIf="heroForm.hasError('required', 'formCtrlName') && formDir.submitted">
  Required!
</small>
 9
Author: AJT_82,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-09 06:31:59

Hay múltiples maneras de resolver el problema. La respuesta de @Splaktar no funcionará si tienes formgroups anidados. Por lo tanto, aquí está la solución que funcionará con grupos de forma anidada.

Solución 1 : Itera a través de todos los formgroups y formcontrols y los toca programáticamente para activar validaciones.

Código de Plantilla:

<form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>
...
<button type="submit" class="btn btn-success">Save</button>
</form>

Componente.código ts:

    onSubmit() {
        if (this.myForm.valid) {
            // save data
        } else {
            this.validateAllFields(this.myForm); 
        }
    }

validateAllFields(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.validateAllFields(control);  
            }
        });
    }

Solución 2 : Utilice una variable para comprobar si formulario ha sido enviado o no. Para su información: El campo enviado para ngForm se está probando actualmente y se incluirá en futuras versiones de Angular. Así que no habrá necesidad de crear su propia variable.

Componente.código ts

private formSubmitAttempt: boolean;

onSubmit() {
        this.formSubmitAttempt = true;
        if (this.myForm.valid) {
            console.log('form submitted');
        }
   }

Código de Plantilla:

<form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>
    <div class="form-group">
        <label class="center-block">
            Name:
            <input class="form-control" formControlName="name">
        </label>
        <div class="alert alert-danger" *ngIf="myForm.get('name').hasError('required') && formSubmitAttempt">
            Name is required
        </div>
        ...
</form>
 9
Author: Sangram Nandkhile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-18 09:12:58

Volviendo después de algunos meses, comparto aquí la versión mejorada basada en todos los comentarios, solo para que conste:

markAsTouched(group: FormGroup | FormArray) {
  Object.keys(group.controls).map((field) => {
    const control = group.get(field);
    if (control instanceof FormControl) {
      control.markAsTouched({ onlySelf: true });
    } else if (control instanceof FormGroup) {
      this.markAsTouched(control);
    }
  });
}

Espero que sea útil!

 5
Author: Mateo Tibaquirá,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-25 20:28:24

Encontré algo que podría ser de interés:

En submit establezco el submitAttempt = true y lo pongo en el div donde debería tener lugar la validación:

nickname.touched || nickname.dirty || (nickname.untouched && submitAttempt)

Significado: Si no se ha tocado, y tratamos de enviar, se muestra el error.

 2
Author: L.querter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-18 14:53:18

Esto se puede lograr a través de markAsTouched():

Object.keys(this.myFormGroup.controls).map((controlName) => {
  this.myFormGroup.get(controlName).markAsTouched({onlySelf: true});
});

Esto no se encontró en el AbstractControl docs, pero está en el código fuente .

 2
Author: Splaktar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-26 06:14:21

Mi aplicación tiene muchos formularios y entradas, por lo que he creado varios componentes de formularios personalizados (para entradas de texto normales, entradas de área de texto, selecciones, casillas de verificación, etc.).) para que no tenga que repetir HTML/CSS detallado y la lógica de la interfaz de usuario de validación de formularios por todas partes.

Mi componente de formulario base personalizado busca su alojamiento FormGroupDirective y usa su propiedad submitted además de sus estados FormControl (valid, touched, etc.) para decidir qué estado de validación y mensaje (en su caso) debe mostrarse en el UI.

Esta solución

  • no requiere atravesar los controles del formulario y modificar su estado
  • no requiere agregar alguna propiedad adicional submitted a cada control
  • no requiere ningún tratamiento adicional de validación de formularios en los métodos ngSubmit-binded onSubmit
  • no combina formularios basados en plantillas con formularios reactivos formularios

Forma-base.componente:

import {Host, Input, OnInit, SkipSelf} from '@angular/core';
import {FormControl, FormGroupDirective} from '@angular/forms';


export abstract class FormBaseComponent implements OnInit {

  @Input() id: string;
  @Input() label: string;
  formControl: FormControl;

  constructor(@Host() @SkipSelf()
              private formControlHost: FormGroupDirective) {
  }

  ngOnInit() {
    const form = this.formControlHost.form;
    this.formControl = <FormControl>form.controls[this.id];
    if (!this.formControl) {
      throw new Error('FormControl \'' + this.id + '\' needs to be defined');
    }
  }

  get errorMessage(): string {
    // TODO return error message based on 'this.formControl.errors'
    return null;
  }

  get showInputValid(): boolean {
    return this.formControl.valid && (this.formControl.touched || this.formControlHost.submitted);
  }

  get showInputInvalid(): boolean {
    return this.formControl.invalid && (this.formControl.touched || this.formControlHost.submitted);
  }
}

Formulario-texto.componente:

import {Component} from '@angular/core';
import {FormBaseComponent} from '../form-base.component';

@Component({
  selector: 'yourappprefix-form-text',
  templateUrl: './form-text.component.html'
})
export class FormTextComponent extends FormBaseComponent {

}

Formulario-texto.componente.html:

<label class="x_label" for="{{id}}">{{label}}</label>
<div class="x_input-container"
     [class.x_input--valid]="showInputValid"
     [class.x_input--invalid]="showInputInvalid">
  <input class="x_input" id="{{id}}" type="text" [formControl]="formControl">
  <span class="x_input--error-message" *ngIf="errorMessage">{{errorMessage}}</span>
</div>

Uso:

<form [formGroup]="form" novalidate>
  <yourappprefix-form-text id="someField" label="Some Field"></yourappprefix-form-text>
</form>
 0
Author: bcody,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-07 15:43:36