web-dev-qa-db-fra.com

Les formes réactives angulaires 2 déclenchent la validation lors de l'envoi

existe-t-il un moyen de déclencher tous les validateurs de formulaires réactifs lors de la soumission, et pas seulement par les événements "sales" et "tactiles"?

La raison en est que nous avons des formulaires très volumineux qui n'indiquent pas si un champ est obligatoire ou non, et que l'utilisateur risque de manquer certains des contrôles nécessaires. par l'utilisateur final sera montré.

J'ai essayé de marquer le formulaire comme "touché" en utilisant le

FormGroup.markAsTouched(true);

cela a fonctionné, et j'ai donc aussi essayé de le marquer comme "sale"

FormGroup.markAsDirty(true);

mais le css de la classe est toujours "ng-primate",

Y at-il un moyen de le déclencher manuellement depuis le composant, j'ai essayé de le googler en vain, merci d'avance!

METTRE À JOUR

Je l'ai déjà fonctionné en itérant les contrôles FormGroup et en les qualifiant de "sales", mais existe-t-il un moyen "standard" de le faire.

26
arvstracthoughts

Ceci peut être réalisé avec l'échantillon présenté here, où vous pouvez utiliser la directive NgForm:

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

et dans vos messages de validation, vérifiez simplement si le formulaire est soumis:

<small *ngIf="heroForm.hasError('required', 'formCtrlName') && formDir.submitted">
  Required!
</small>
14
AJT_82

Il y a plusieurs façons de résoudre le problème. La réponse de @ Splaktar ne fonctionnera pas si vous avez des groupes de formulaires imbriqués. Alors, voici la solution qui fonctionnera avec les groupes de formulaires imbriqués.

Solution 1 : Parcourez tous les groupes de formulaires et contrôles de formulaires et touchez-les par programme pour déclencher des validations.

Code du modèle:

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

composant.ts code:

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

Solution 2 : Utilisez une variable pour vérifier si le formulaire a été soumis ou non. FYI: Le champ soumis pour ngForm est en cours de test et sera inclus dans les futures versions angulaires. Il ne sera donc pas nécessaire de créer votre propre variable.

composant.ts code

private formSubmitAttempt: boolean;

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

Code du modèle:

<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>
10
Sangram Nandkhile

Revenant après quelques mois, je partage ici la version améliorée basée sur tous les commentaires, juste pour l'enregistrement:

markAsTouched(group: FormGroup | FormArray) {
  group.markAsTouched({ onlySelf: true });

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

J'espère que ça vous sera utile!

9
Mateo Tibaquirá

Ceci peut être accompli via markAsTouched() . Jusqu'à ce que PR # 26812 soit fusionné, vous pouvez utiliser

function markAllAsTouched(group: AbstractControl) {
  group.markAsTouched({onlySelf: true});
  group._forEachChild((control: AbstractControl) => markAllAsTouched(control));
}

Vous pouvez en savoir plus dans le code source .

4
Splaktar

Mon application contient de nombreux formulaires et entrées. J'ai donc créé divers composants de formulaire personnalisés (pour les entrées de texte normales, les entrées de texte, les sélections, les cases à cocher, etc.), de sorte que je n'ai pas besoin de répéter le code HTML/CSS détaillé et la logique de l'interface de validation de formulaire tous sur le lieu.

Mon composant de base personnalisé recherche son hébergement FormGroupDirective et utilise sa propriété submitted en plus de ses états FormControl (valid, touched, etc.) pour décider quel statut de validation et quel message (le cas échéant) doit être affiché sur l'interface utilisateur.

Cette solution 

  • ne nécessite pas de parcourir les contrôles du formulaire et de modifier leur statut
  • ne nécessite pas l'ajout d'une propriété submitted supplémentaire à chaque contrôle
  • ne nécessite aucune manipulation de validation de formulaire supplémentaire dans les méthodes ngSubmit- binded onSubmit
  • ne combine pas les formulaires basés sur des modèles avec des formulaires réactifs

form-base.component:

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

form-text.component:

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 {

}

form-text.component.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>

Utilisation:

<form [formGroup]="form" novalidate>
  <yourappprefix-form-text id="someField" label="Some Field"></yourappprefix-form-text>
</form>
1
bcody

"sale", "touché", "soumis" peut être combiné selon la méthode suivante:

<form [formGroup]="form" (ngSubmit)="doSomething()" #ngForm="ngForm">
<input type="text" placeholder="Put some text" formControlName="textField" required>
<div *ngIf="textField.invalid && (textField.dirty || textField.touched || ngForm.submitted)">
  <div *ngIf="textField.errors.required">Required!</div>
</div>
<input type="submit" value="Submit" />
</form>
0
eshangin

Si j'obtiens ce que vous demandez. Vous souhaitez uniquement mettre à jour les messages de validation à chaque envoi. La meilleure façon de procéder consiste à stocker l'historique de l'état de contrôle.

export interface IValdiationField {
  submittedCount: number;
  valid: boolean;
}
class Component {
   // validation state management
   validationState: Map<string, IValdiationField | number> = new Map();

   constructor() {
      this.validationState.set('submitCount', 0);
   }

   validationChecker(formControlName: string): boolean {

    // get submitted count
    const submittedCount: number = (this.validationState.get('submitCount') || 0) as number;

    // form shouldn't show validation if form not submitted
    if (submittedCount === 0) {
       return true;
    }

    // get the validation state
    const state: IValdiationField = this.validationState.get(formControlName) as IValdiationField;

    // set state if undefined or state submitted count doesn't match submitted count
    if (state === undefined || state.submittedCount !== submittedCount) {
       this.validationState.set(formControlName, { submittedCount, valid: this.form.get(formControlName).valid } );
       return this.form.get(formControlName).valid;
     }

        // get validation value from validation state managment
       return state.valid;
   }
   submit() {
     this.validationState.set('submitCount', (this.validationState.get('submitCount') as number) + 1);
   } 
}

Ensuite, dans le code html * ngIf = "! ValidationChecker ('formControlName')" pour afficher le message d'erreur.

0
Jordan Hall