110 votes

Formulaires réactifs - marquer les champs comme touchés

J'ai du mal à trouver comment marquer tous les champs du formulaire comme étant touchés. Le problème principal est que si je ne touche pas les champs et que j'essaie de soumettre le formulaire, l'erreur de validation ne s'affiche pas. J'ai un espace réservé pour ce morceau de code dans mon contrôleur.
Mon idée est simple :

  1. l'utilisateur clique sur le bouton d'envoi
  2. tous les champs marqués comme touchés
  3. le formateur d'erreurs réexécute et affiche les erreurs de validation

Si quelqu'un a d'autres idées sur la façon d'afficher les erreurs lors de l'envoi, sans mettre en œuvre une nouvelle méthode, veuillez les partager. Merci !


Ma forme simplifiée :

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

Et mon contrôleur :

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

162voto

jtsan Points 115

La fonction suivante parcourt les contrôles d'un groupe de formulaires et les touche délicatement. Comme le champ des contrôles est un objet, le code appelle Object.values() sur le champ des contrôles du groupe de formulaires.

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

      if (control.controls) {
        this.markFormGroupTouched(control);
      }
    });
  }

20 votes

Malheureusement, cela ne fonctionne pas dans Internet Explorer :( il suffit de modifier (<any>Object).values(formGroup.controls) a Object.keys(formGroup.controls).map(x => formGroup.controls[x]) (de stackoverflow.com/questions/42830257/ )

1 votes

Cela a été d'une grande aide pour moi qui utilise FormGroup et FormControl et qui me demande comment montrer à l'utilisateur qu'il n'a pas touché un champ obligatoire. Merci.

0 votes

@NAMS pas de problème ! Je suis content que ça ait aidé :]

14voto

GarryOne Points 485

En ce qui concerne la réponse de @masterwork. J'ai essayé cette solution, mais j'ai eu une erreur quand la fonction a essayé de creuser, récursivement, à l'intérieur d'un FormGroup, parce qu'il passe un argument FormControl, au lieu d'un FormGroup, à cette ligne :

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

Voici ma solution

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

8voto

jsertx Points 456

Le fait de passer en revue les contrôles du formulaire et de les marquer comme touchés pourrait également fonctionner :

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

1 votes

Merci mon pote, ta solution est très bonne, la seule chose que je voudrais ajouter parce que tslint se plaint est la suivante : for (const i in this.form.controls) { if (this.form.controls[i]) { this.form.controls[i].markAsTouched() ; } }. }

1 votes

Cela ne fonctionne pas si votre formGroup contient d'autres formGroup s

3voto

Aladdin Homs Points 818

Voici ma solution

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

1voto

Vlado Tesanovic Points 1505
onSubmit(form: any): void {
  if (!this.form) {
    this.form.markAsTouched();
    // this.form.markAsDirty(); <-- this can be useful 
  }
}

0 votes

Je viens d'essayer cela et d'une manière ou d'une autre, cela ne touche pas les éléments de formulaire enfant. J'ai dû écrire une boucle qui marque tous les éléments enfants manuellement. Avez-vous une idée de la raison pour laquelle markAsTouched() ne touche pas les éléments enfants ?

0 votes

Quelles versions d'Angular utilisez-vous ?

0 votes

La version d'Angular est 2.1.0

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X