Tengo problemas para averiguar cómo marcar todos los campos del formulario como tocados. El principal problema es que si no toco los campos e intento enviar el formulario, el error de validación no aparece. Tengo un marcador de posición para ese fragmento de código en mi controlador.
Mi idea es simple:
- el usuario hace clic en el botón enviar
- todos los campos marcan como tocados
- el formateador de errores vuelve a ejecutar y muestra errores de validación
Si alguien tiene otra idea de cómo mostrar errores al enviar, sin implementar un nuevo método, compártalos. ¡Gracias!
Mi forma simplificada:
<form class="form-horizontal" [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
<input type="text" id="title" class="form-control" formControlName="title">
<span class="help-block" *ngIf="formErrors.title">{{ formErrors.title }}</span>
<button>Submit</button>
</form>
Y mi controlador:
import {Component, OnInit} from '@angular/core';
import {FormGroup, FormBuilder, Validators} from '@angular/forms';
@Component({
selector : 'pastebin-root',
templateUrl: './app.component.html',
styleUrls : ['./app.component.css']
})
export class AppComponent implements OnInit {
form: FormGroup;
formErrors = {
'title': ''
};
validationMessages = {
'title': {
'required': 'Title is required.'
}
};
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
this.buildForm();
}
onSubmit(form: any): void {
// somehow touch all elements so onValueChanged will generate correct error messages
this.onValueChanged();
if (this.form.valid) {
console.log(form);
}
}
buildForm(): void {
this.form = this.fb.group({
'title': ['', Validators.required]
});
this.form.valueChanges
.subscribe(data => this.onValueChanged(data));
}
onValueChanged(data?: any) {
if (!this.form) {
return;
}
const form = this.form;
for (const field in this.formErrors) {
if (!this.formErrors.hasOwnProperty(field)) {
continue;
}
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.touched && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
if (!control.errors.hasOwnProperty(key)) {
continue;
}
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
}
angular
angular-reactive-forms
angular2-forms
angular2-formbuilder
Giedrius Kiršys
fuente
fuente
(<any>Object).values(formGroup.controls)
aObject.keys(formGroup.controls).map(x => formGroup.controls[x])
(de stackoverflow.com/questions/42830257/… )controls
al inicio de la función, por lo que debería ser lo siguiente en su lugar:if (control.controls) { markFormGroupTouched(control); }
touched
solo significa que la entrada fue borrosa una vez. Para que aparecieran errores, también tuve que llamarupdateValueAndValidity()
a mis controles.Desde Angular 8/9 simplemente puede usar
this.form.markAllAsTouched();
Para marcar un control y sus controles descendientes como tocados.
AbstractControl doc
fuente
Con respecto a la respuesta de @ masterwork. Probé esa solución, pero recibí un error cuando la función intentó excavar, de forma recursiva, dentro de un FormGroup, porque se pasa un argumento FormControl, en lugar de un FormGroup, en esta línea:
control.controls.forEach(c => this.markFormGroupTouched(c));
Aqui esta mi solucion
fuente
Desde Angular v8, tiene esto incorporado con la ayuda del
markAllAsTouched
método.Como ejemplo, podrías usarlo como
Consulte el documento oficial: https://angular.io/api/forms/AbstractControl#markallastouched
fuente
Recorrer los controles de formulario y marcarlos como tocados también funcionaría:
fuente
formGroup
contiene otrosformGroup
sEsta es mi solucion
fuente
Tuve este problema pero encontré la forma "correcta" de hacerlo, a pesar de no estar en ningún tutorial de Angular que haya encontrado.
En su HTML, en la
form
etiqueta, agregue la misma Variable de referencia de plantilla (variable#myVariable='ngForm'
'hashtag') que usan los ejemplos de formularios basados en plantillas, además de lo que usan los ejemplos de formularios reactivos:<form [formGroup]="myFormGroup" #myForm="ngForm" (ngSubmit)="submit()">
Ahora tiene acceso a
myForm.submitted
la plantilla que puede usar en lugar de (o además de)myFormGroup.controls.X.touched
:<div *ngIf="myForm.submitted" class="text-error"> <span *ngIf="myFormGroup.controls.myFieldX.errors?.badDate">invalid date format</span> <span *ngIf="myFormGroup.controls.myFieldX.errors?.isPastDate">date cannot be in the past.</span> </div>
Sepa que
myForm.form === myFormGroup
es cierto ... siempre y cuando no olvide la="ngForm"
parte. Si lo usa#myForm
solo, no funcionará porque la var se establecerá en HtmlElement en lugar de la directiva que impulsa ese elemento.Saben que
myFormGroup
es visible en el código de texto mecanografiado de su componente por las formas reactivas tutoriales, peromyForm
no lo es, a menos que se pasa a través de una llamada al método, al igual quesubmit(myForm)
asubmit(myForm: NgForm): void {...}
. (El avisoNgForm
está en mayúsculas en el texto mecanografiado, pero en formato camel en HTML).fuente
fuente
markAsTouched()
no toca los elementos secundarios?markAsTouched()
qué no marcar elementos secundarios: github.com/angular/angular/issues/11774 . TL; DR: No es un error.Me encontré con el mismo problema, pero no quiero "contaminar" mis componentes con el código que maneja esto. Especialmente porque necesito esto en muchas formas y no quiero repetir el código en varias ocasiones.
Por lo tanto, creé una directiva (usando las respuestas publicadas hasta ahora). La directiva decora el método de NgForm
onSubmit
: si el formulario no es válido, marca todos los campos como tocados y cancela el envío. De lo contrario, el método onSubmit habitual se ejecuta normalmente.import {Directive, Host} from '@angular/core'; import {NgForm} from '@angular/forms'; @Directive({ selector: '[appValidateOnSubmit]' }) export class ValidateOnSubmitDirective { constructor(@Host() form: NgForm) { const oldSubmit = form.onSubmit; form.onSubmit = function (): boolean { if (form.invalid) { const controls = form.controls; Object.keys(controls).forEach(controlName => controls[controlName].markAsTouched()); return false; } return oldSubmit.apply(form, arguments); }; } }
Uso:
<form (ngSubmit)="submit()" appValidateOnSubmit> <!-- ... form controls ... --> </form>
fuente
Este es el código que estoy usando.
fuente
Este código funciona para mí:
fuente
Una solución sin recursividad
Para aquellos preocupados por el rendimiento, se me ocurrió una solución que no usa la recursividad, aunque todavía itera sobre todos los controles en todos los niveles.
Esta solución funciona tanto en FormGroup como en FormArray.
Puedes jugar con él aquí: angular-mark-as-touch
fuente
según @masterwork
código mecanografiado para la versión angular 8
fuente
Así es como lo hago. No quiero que se muestren los campos de error hasta que se presione el botón de envío (o se toque el formulario).
import {FormBuilder, FormGroup, Validators} from "@angular/forms"; import {OnInit} from "@angular/core"; export class MyFormComponent implements OnInit { doValidation = false; form: FormGroup; constructor(fb: FormBuilder) { this.form = fb.group({ title: ["", Validators.required] }); } ngOnInit() { } clickSubmitForm() { this.doValidation = true; if (this.form.valid) { console.log(this.form.value); }; } }
<form class="form-horizontal" [formGroup]="form" > <input type="text" class="form-control" formControlName="title"> <div *ngIf="form.get('title').hasError('required') && doValidation" class="alert alert-danger"> title is required </div> <button (click)="clickSubmitForm()">Submit</button> </form>
fuente
Entiendo completamente la frustración del OP. Yo uso lo siguiente:
Función de utilidad :
Uso :
Tenga en cuenta que esta función aún no se adapta a los controles anidados.
fuente
Vea esta joya . Hasta ahora, la solución más elegante que he visto.
Código completo
fuente
fuente
Ver:
API
fuente
Hice una versión con algunos cambios en las respuestas presentadas, para aquellos que estén usando versiones anteriores a la versión 8 del angular, me gustaría compartirla con aquellos que sean útiles.
Función de utilidad:
Uso:
fuente