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.
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>
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>
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!
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 .
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
submitted
supplémentaire à chaque contrôlengSubmit
- binded onSubmit
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>
"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>
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.