J'ai du mal à utiliser à la fois * ngIf dans un formulaire et ngFormModel pour valider ledit formulaire.
Le cas d'utilisation est le suivant: en fonction des entrées de l'utilisateur, masquez ou désactivez certains champs spécifiques du formulaire. Mais si ces entrées sont affichées, elles doivent être validées.
Lorsque seule la validation de base est requise, je peux effectuer d'une manière ou d'une autre:
ngControl
et required
pattern
. Ce n'est pas angulaire, mais ça marche.Mais pour implémenter des validations plus complexes, j'ai essayé d'utiliser ngControl
couplé à ngFormModel
afin d'utiliser des contrôles personnalisés. J'ai utilisé des morceaux de code trouvés dans les pages suivantes:
Comment ajouter un modèle de validation de formulaire dans angular2 (et les liens qui y sont référencés)
Angular2 Forms: Validations, ngControl, ngModel etc.
Mon code est le suivant:
HTML
<div>
<h1>Rundown of the problem</h1>
<form (ngSubmit)="submitForm()" #formState="ngForm" [ngFormModel]="myForm">
<div class="checkbox">
<label>
<input type="checkbox" [(ngModel)]="model.hideField" ngControl="hideField"> Is the input below useless for you ?
</label>
</div>
<div *ngIf="!model.hideField">
<div class="form-group">
<label for="optionalField">Potentially irrelevant field </label>
<input type="text" class="form-control" [(ngModel)]="model.optionalField" ngControl="optionalField" required #optionalField="ngForm">
<div [hidden]="optionalField.valid || optionalField.pristine" class="alert alert-warning">
This input must go through myCustomValidator(), so behave.
</div>
</div>
</div>
<button type="submit" class="btn btn-primary" [disabled]="!formState.form.valid">I can't be enabled without accessing the input :(</button>
<button type="submit" class="btn btn-default">Submit without using form.valid (boo !)</button>
</form>
</div>
TypeScript
import {Component, ChangeDetectorRef, AfterViewInit } from 'angular2/core';
import {NgForm, FormBuilder, Validators, ControlGroup, FORM_DIRECTIVES} from 'angular2/common';
@Component({
selector: 'accueil',
templateUrl: 'app/accueil.component.bak.html',
directives:[FORM_DIRECTIVES],
providers: [FormBuilder]
})
export class AccueilComponent implements AfterViewInit {
private myForm: ControlGroup;
model: any;
constructor(fb: FormBuilder, cdr: ChangeDetectorRef) {
this.cdr = cdr ;
this.model = {} ;
this.myForm = fb.group({
"hideField": [false],
"optionalField": [this.model.optionalField, Validators.compose([this.myCustomValidator])]
});
}
ngAfterViewInit() {
// Without this, I get the "Expression has changed after it was checked" exception.
// See also : https://stackoverflow.com/questions/34364880/expression-has-changed-after-it-was-checked
this.cdr.detectChanges();
}
submitForm(){
alert("Submitted !");
}
myCustomValidator(optionalField){
// Replace "true" by "_someService.someCheckRequiringLogicOrData()"
if(true) {
return null;
}
return { "ohNoes": true };
}
}
Même si l'entrée est supprimée du modèle avec * ngIf, le constructeur fait toujours référence au contrôle. Ce qui à mon tour m'empêche d'utiliser [disabled] = "! FormState.form.valid", car myForm
est naturellement INVALID.
Est-ce que je vise autant que possible en utilisant Angular 2? Je suis sûr que ce n'est pas si rare qu'un cas d'utilisation, mais encore une fois, avec mes connaissances actuelles, je ne vois pas comment je pourrais le faire fonctionner.
Merci !
Ce que vous pouvez essayer de faire est de réinitialiser les validateurs sur votre contrôle. Ce qui signifie que si vous souhaitez qu'un nouvel ensemble de validateurs soit lié à un contrôle en raison d'un changement d'état, vous devez redéfinir la fonction de validation.
Dans votre cas, lorsque votre case à cocher est cochée/décochée, vous souhaitez que les événements suivants se produisent:
form.valid
soit mis à jour.Voir mon exemple de plnkr basé sur le guide des formulaires d'Angular.io
if (optional)
this.heroFormModel.controls['name'].validator = Validators.minLength(3);
else
this.heroFormModel.controls['name'].validator =
Validators.compose([Validators.minLength(3), Validators.required]);
this.heroFormModel.controls['name'].updateValueAndValidity();
Je viens de rencontrer exactement le même problème, et j'ai trouvé une solution de contournement qui repose sur incluant et excluant manuellement les contrôles:
import {Directive, Host, SkipSelf, OnDestroy, Input, OnInit} from 'angular2/core';
import {ControlContainer} from 'angular2/common';
@Directive({
selector: '[ngControl]'
})
export class MyControl implements OnInit, OnDestroy {
@Input() ngControl:string;
constructor(@Host() @SkipSelf() private _parent:ControlContainer) {}
ngOnInit():void {
// see https://github.com/angular/angular/issues/6005
setTimeout(() => this.formDirective.form.include(this.ngControl));
}
ngOnDestroy():void {
this.formDirective.form.exclude(this.ngControl);
}
get formDirective():any {
return this._parent.formDirective;
}
}
Pour que cela fonctionne, tous les contrôles dynamiques doivent d'abord être exclus du formulaire; voir le plunkr pour plus de détails.
Voici une version mise à jour pour RC4. Je l'ai également renommé npControl pour mes besoins.
import { Directive, Host, OnDestroy, Input, OnInit } from '@angular/core';
import { ControlContainer } from '@angular/forms';
@Directive({
selector: '[npControl]'
})
export class NPControlDirective implements OnInit, OnDestroy {
@Input() npControl: string;
constructor(@Host() private _parent: ControlContainer
) { }
ngOnInit(): void {
console.log('include ', this.npControl);
setTimeout(() => this.formDirective.form.include(this.npControl));
}
ngOnDestroy(): void {
console.log('exclude ', this.npControl);
this.formDirective.form.exclude(this.npControl);
}
get formDirective(): any {
return this._parent;
}
}