Mam problem ze znalezieniem sposobu oznaczenia wszystkich pól formularza jako dotkniętych. Główny problem polega na tym, że jeśli nie dotykam pól i nie próbuję wysłać formularza - błąd walidacji nie pojawia się. Mam symbol zastępczy dla tego fragmentu kodu w moim kontrolerze.
Mój pomysł jest prosty:
- użytkownik klika przycisk przesyłania
- wszystkie pola są zaznaczone jako dotknięte
- formater błędów uruchamia się ponownie i wyświetla błędy walidacji
Jeśli ktoś ma inny pomysł, jak pokazać błędy przy przesyłaniu, bez implementacji nowej metody - podziel się nimi. Dzięki!
Mój uproszczony formularz:
<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>
I mój kontroler:
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
źródło
źródło
(<any>Object).values(formGroup.controls)
naObject.keys(formGroup.controls).map(x => formGroup.controls[x])
(ze stackoverflow.com/questions/42830257/… )controls
na początku funkcji, więc zamiast tego powinno wyglądać następująco:if (control.controls) { markFormGroupTouched(control); }
touched
oznacza po prostu, że wejście zostało raz zamazane. Aby pojawiły się błędy, musiałem również wywołaćupdateValueAndValidity()
moje kontrolki.Od Angular 8/9 możesz po prostu użyć
this.form.markAllAsTouched();
Aby oznaczyć kontrolkę i jej elementy podrzędne jako dotknięte.
AbstractControl doc
źródło
Odnośnie odpowiedzi @ masterwork. Wypróbowałem to rozwiązanie, ale wystąpił błąd, gdy funkcja próbowała rekursywnie kopać wewnątrz FormGroup, ponieważ w tym wierszu jest przekazywany argument FormControl zamiast FormGroup:
control.controls.forEach(c => this.markFormGroupTouched(c));
Oto moje rozwiązanie
źródło
Od Angular v8 masz to wbudowane za pomocą tej
markAllAsTouched
metody.Jako przykład możesz użyć tego jak
Zobacz oficjalny dokument: https://angular.io/api/forms/AbstractControl#markallastouched
źródło
Zapętlenie przez kontrolki formularza i oznaczenie ich jako dotkniętych również zadziała:
źródło
formGroup
zawiera inneformGroup
sTo jest moje rozwiązanie
źródło
Miałem ten problem, ale znalazłem „właściwy” sposób, aby to zrobić, mimo że nie ma go w żadnym samouczku dotyczącym Angulara, jaki kiedykolwiek znalazłem.
W
form
tagu HTML dodaj tę samą zmienną odniesienia szablonu#myVariable='ngForm'
( zmienną „hashtag”), której używają przykłady formularzy sterowanych szablonami, oprócz tego, czego używają przykłady formularzy reaktywnych:<form [formGroup]="myFormGroup" #myForm="ngForm" (ngSubmit)="submit()">
Teraz masz dostęp
myForm.submitted
w szablonie, do którego możesz użyć zamiast (lub dodatkowo)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>
Wiedz, że
myForm.form === myFormGroup
to prawda ... o ile nie zapomnisz="ngForm"
części. Jeśli użyjesz#myForm
samego, nie zadziała, ponieważ var zostanie ustawiony na HtmlElement zamiast dyrektywy sterującej tym elementem.Wiedz o tym
myFormGroup
jest widoczny na maszynie kodu komponentu w jednej z reaktywnych form ćwiczeń, alemyForm
nie jest, chyba że przechodzą go poprzez wywołanie metody, jaksubmit(myForm)
sięsubmit(myForm: NgForm): void {...}
. (UwagaNgForm
w maszynopisie jest zapisana wielkimi literami, a w HTML - wielbłądem).źródło
źródło
markAsTouched()
nie dotknął elementów potomnych?markAsTouched()
nie oznaczać elementów potomnych - github.com/angular/angular/issues/11774 . TL; DR: To nie jest błąd.Napotkałem ten sam problem, ale nie chcę „zanieczyszczać” moich komponentów kodem, który to obsługuje. Zwłaszcza, że potrzebuję tego w wielu formach i nie chcę przy różnych okazjach powtarzać kodu.
W ten sposób stworzyłem dyrektywę (korzystając z dotychczas zamieszczonych odpowiedzi). Dyrektywa ozdabia -Method
onSubmit
NgForm: Jeśli formularz jest nieprawidłowy, oznacza wszystkie pola jako dotknięte i przerywa przesyłanie. W przeciwnym razie zwykła metoda onSubmit jest wykonywana normalnie.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); }; } }
Stosowanie:
<form (ngSubmit)="submit()" appValidateOnSubmit> <!-- ... form controls ... --> </form>
źródło
To jest kod, którego faktycznie używam.
źródło
Ten kod działa dla mnie:
źródło
Rozwiązanie bez rekursji
Dla tych, którzy martwią się wydajnością, wymyśliłem rozwiązanie, które nie używa rekursji, chociaż nadal iteruje po wszystkich kontrolkach na wszystkich poziomach.
To rozwiązanie działa zarówno z FormGroup, jak i FormArray.
Możesz się nim bawić tutaj: kątowy znak jako dotknięty
źródło
zgodnie z @masterwork
kod maszynowy dla wersji kątowej 8
źródło
Oto jak to robię. Nie chcę, aby pola błędów były wyświetlane, dopóki nie zostanie naciśnięty przycisk przesyłania (lub formularz).
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>
źródło
Całkowicie rozumiem frustrację OP. Używam następujących:
Funkcja użytkowa :
Użycie :
Zauważ, że ta funkcja nie obsługuje jeszcze zagnieżdżonych kontrolek.
źródło
Zobacz ten klejnot . Jak dotąd najbardziej eleganckie rozwiązanie, jakie widziałem.
Pełny kod
źródło
źródło
Widok:
API
źródło
Zrobiłem wersję z pewnymi zmianami w przedstawionych odpowiedziach, dla tych, którzy używają wersji starszej niż wersja 8 angulara, chciałbym się nią podzielić z tymi, którzy są przydatni.
Funkcja użytkowa:
Stosowanie:
źródło