2016-11-10 18 views
14

Mam problem ze znalezieniem sposobu oznaczania wszystkich pól formularza jako dotkniętych. Głównym problemem jest to, że jeśli nie dotknę pól i spróbuję wysłać formularz - błąd sprawdzania nie zostanie wyświetlony. Mam symbol zastępczy dla tego fragmentu kodu w moim kontrolerze.
Mój pomysł jest prosty: kliknięćFormularze reaktywne - zaznacz pola jako dotknięte

  1. użytkownika przycisk przesyłania
  2. wszystkie znaki dziedzinach jak dotknął
  3. powtórek błąd formater i wyświetla błędy sprawdzania poprawności

Jeśli ktoś ma inny pomysł jak pokazać błędy po przesłaniu, bez wprowadzania nowej metody - prosimy o ich udostępnienie. Dzięki!


Moja uproszczona forma:

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

A 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] + ' '; 
     } 
     } 
    } 
    } 
} 

Odpowiedz

23

Następujące funkcje recurses kontrolując w postaci grupy i delikatnie je dotyka. Ponieważ pole sterujące jest obiektem, wywołanie kodu Object.values ​​() w polu kontrolnym grupy formularzy.

/** 
    * Marks all controls in a form group as touched 
    * @param formGroup - The group to caress..hah 
    */ 
    private markFormGroupTouched(formGroup: FormGroup) { 
    (<any>Object).values(formGroup.controls).forEach(control => { 
     control.markAsTouched(); 

     if (control.controls) { 
     control.controls.forEach(c => this.markFormGroupTouched(c)); 
     } 
    }); 
    } 
+3

to niestety nie działa w Internet Explorerze :(po prostu zmień '( Obiekt) .values ​​(formGroup.controls)' na 'Object.keys (formGroup.controls) .map (x => formGroup.controls [x])' (od https://stackoverflow.com/questions/42830257/alternative-version-for-object-values) –

+0

To była ogromna pomoc dla mnie przy użyciu FormGroup i FormControl i zastanawiasz się, jak pokazać użytkownikowi, że nie dotknął Wymagane pole Dziękujemy. – NAMS

1
onSubmit(form: any): void { 
    if (!this.form) { 
    this.form.markAsTouched(); 
    // this.form.markAsDirty(); <-- this can be useful 
    } 
} 
+0

Po prostu wypróbowałem to i jakoś nie dotyka elementów formy dziecięcej.Musiał napisać pętlę, która ręcznie zaznacza wszystkie elementy potomne. Czy masz jakąś wskazówkę dlaczego 'markAsTouched()' nie dotyka elementów potomnych? –

+0

Jakie wersje kątowe są używane? –

+0

Wersja kątowa to 2.1.0 –

1

Oto jak to zrobić. Nie chcę, aby pola błędów były wyświetlane do momentu naciśnięcia przycisku przesyłania (lub dotknięcia formularza).

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>

+0

Wygląda na to, że z czasem może stać się ciężki przy dodawaniu nowych reguł sprawdzania poprawności. Ale mam rację. –

0

Całkowicie rozumiem frustrację PO. Używam następujące:

funkcji użyteczności:

/** 
* Determines if the given form is valid by touching its controls 
* and updating their validity. 
* @param formGroup the container of the controls to be checked 
* @returns {boolean} whether or not the form was invalid. 
*/ 
export function formValid(formGroup: FormGroup): boolean { 
    return !Object.keys(formGroup.controls) 
    .map(controlName => formGroup.controls[controlName]) 
    .filter(control => { 
     control.markAsTouched(); 
     control.updateValueAndValidity(); 
     return !control.valid; 
    }).length; 
} 

Usage:

onSubmit() { 
    if (!formValid(this.formGroup)) { 
    return; 
    } 
    // ... TODO: logic if form is valid. 
} 

pamiętać, że ta funkcja nie jest jeszcze zaspokoić zagnieżdżonych kontroli.

0

Miałem ten problem, ale znalazłem "poprawny" sposób, mimo że nie byłam w żadnym tutorialu Angular, jaki kiedykolwiek znalazłam.

w HTML, na metce form, dodać taką samą Reference Zmienna szablonu #myVariable='ngForm' (zmienna „hashtag”), że przykłady form Template-Driven używania, oprócz tego, co reaktywnych form przykłady użycia:

<form [formGroup]="myFormGroup" #myForm="ngForm" (ngSubmit)="submit()">

teraz masz dostęp do myForm.submitted w szablonie, który można użyć zamiast (lub oprócz) 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>

Wiesz, że myForm.form === myFormGroup jest prawdą ... o ile nie zapomnisz części o numerze ="ngForm". Jeśli korzystasz tylko z wersji #myForm, nie będzie działać, ponieważ zmienna zostanie ustawiona na HtmlElement zamiast na dyrektywę sterującą tym elementem.

Wiedz, że myFormGroup jest widoczny w kodzie maszynopis komponentu jednej z reaktywnych form ćwiczeń, ale myForm nie jest, chyba że przechodzą go poprzez wywołanie metody, jak submit(myForm) do submit(myForm: NgForm): void {...}. (Zawiadomienie NgForm jest w tytułowych czapki w maszynopisie ale wielbłąda przypadku w HTML).

2

To jest moje rozwiązanie

 static markFormGroupTouched (FormControls: { [key: string]: AbstractControl } | AbstractControl[]): void { 
     const markFormGroupTouchedRecursive = (controls: { [key: string]: AbstractControl } | AbstractControl[]): void => { 
      _.forOwn(controls, (c, controlKey) => { 
      if (c instanceof FormGroup || c instanceof FormArray) { 
       markFormGroupTouchedRecursive(c.controls); 
      } else { 
       c.markAsTouched(); 
      } 
      }); 
     }; 
     markFormGroupTouchedRecursive(FormControls); 
     } 
0

wpadłem na ten sam problem, ale nie chcę, aby „zanieczyścić” moje komponenty z kodem, który to obsługuje. Zwłaszcza, że ​​potrzebuję tego w wielu postaciach i nie chcę powtarzać kodu przy różnych okazjach.

W ten sposób stworzyłem dyrektywę (korzystając z opublikowanych do tej pory odpowiedzi). Dyrektywa dekoruje NgForm's onSubmit -Metoda: Jeśli formularz jest nieprawidłowy, oznacza wszystkie pola jako dotknięte i przerywa składanie. W przeciwnym razie zwykle metoda onSubmit działa 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); 
     }; 
    } 
} 

Zastosowanie:

<form (ngSubmit)="submit()" appValidateOnSubmit> 
    <!-- ... form controls ... --> 
</form> 
0

Jest to kod, że jestem rzeczywiście używany.

validateAllFormFields(formGroup: any) { 
 
    // This code also works in IE 11 
 
    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.validateAllFormFields(control); 
 
     } else if (control instanceof FormArray) { 
 
      this.validateAllFormFields(control); 
 
     } 
 
    }); 
 
}

1

Przelotowe kontroli formularzy i ich oznakowanie dotknął również pracować:

for(let i in this.form.controls) 
    this.form.controls[i].markAsTouched(); 
+0

Dzięki, że twoje rozwiązanie jest całkiem dobre, jedyne co mógłbym dodać, ponieważ tslint narzeka: dla (const i in this.form.controls) { if (this.form.controls [i ]) { this.form.controls [i] .markAsTouched(); } } –

-1

Ten kod działa dla mnie:

markAsRequired(formGroup: FormGroup) { 
    if (Reflect.getOwnPropertyDescriptor(formGroup, 'controls')) { 
    (<any>Object).values(formGroup.controls).forEach(control => { 
     if (control instanceof FormGroup) { 
     // FormGroup 
     markAsRequired(control); 
     } 
     // FormControl 
     control.markAsTouched(); 
    }); 
    } 
} 
1

odniesieniu do @ Odpowiedź magistra. próbowałem tego rozwiązania, ale mam błąd, gdy funkcja próbował kopać, rekurencyjnie, wewnątrz FormGroup, bo nie przechodzi argument FormControl, zamiast FormGroup, w tym wierszu:

control.controls.forEach(c => this.markFormGroupTouched(c));

Oto moje rozwiązanie:

markFormGroupTouched(formGroup: FormGroup) { 
(<any>Object).values(formGroup.controls).forEach(control => { 
    if (control.controls) { // control is a FormGroup 
    markFormGroupTouched(control); 
    } else { // control is a FormControl 
    control.markAsTouched(); 
    } 
}); 
} 
Powiązane problemy