I'm having a hard time trying to use both *ngIf inside a form, and ngFormModel to validate said form.
Use case is this : based on user input, hide or deactivate some specific fields in the form. But in case these inputs are shown, they must be validated.
When only basic validation is required, I can make do one way or another :
- If the input is only required, it works A-OK using
ngControl
andrequired
- If a specific format is necessary, it's possible to use the
pattern
attribute. It's not Angular, but it works.
But in order to implement more complex validations, I've been trying to use ngControl
coupled to ngFormModel
so as to use custom checks. I used pieces of code found on the following pages :
How to add form validation pattern in angular2 (and the links referenced there)
Angular2 Forms :Validations, ngControl, ngModel etc
My code is as follows :
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 };
}
}
Even if the input is removed from the template with *ngIf, the constructor is still referencing the control. Which in turns prevents me from using the [disabled]="!formState.form.valid", as myForm
is understandably INVALID.
Is what I am aiming at possible using Angular 2 ? I'm sure this is not that uncommon a use case, but then again with my current knowledge, I cannot see how I could make it work.
Thanks !