Form
Template-driven
Set up
use ngModel
in the built-in Angular Directive: FormsModule
In HTML:
-
we need
form
,input
,button
-
bind local ref to
form
, and assign it asngForm
#myForm="ngForm"
-
bind submit function to
ngSubmit
.<form #myForm="ngForm" (ngSubmit)="onSubmit()">
-
bind
ngModel
toinput
, and addname
attribute<input name="email" ngModel/>
<form #myForm="ngForm" (ngSubmit)="onSubmit()">
<input name="email" ngModel/>
<button type="submit">Submit</button>
</form>
In Component.ts:
@Compoent({
// ...
})
export class AComponent {
@ViewChild('myForm') myForm: NgForm;
onSubmit(){}
}
Validation
By default, Angular disables HTML5 validation, and uses its validators, like:
- required
- ...
<input name="email" ngModel email required/>
To show error message:
-
bind the local ref of
input
withngModel
, like what is done toform
-
use
ngIf
to check if theinput
is validated
<input #email="ngModel" name="email" ngModel email required/>
<span *ngIf="email.invalid && email.touched">wrong email</span>
input.ng-invalid.ng-touched {
border: 1px solid red;
}
Grouping Form Control
We can control a group of inputs:
<form #myForm="ngForm" (ngSubmit)="onSubmit()">
<div #myGroupCtrl="ngModelGroup" ngModelGroup="userData">
<!-- some <input /> -->
</div>
<!-- show ngModelGroup error -->
<p *ngIf="myGroupCtrl.invalid">user data is invalid.</p>
</form>
@ViewChild('myForm') myForm: NgForm;
// the data of ngModelGroup is saved in myForm
const myGroup = this.myForm.value.userData
Set value of Form
We set some form fields without affecting other fields by using ngForm.form.patchValue()
.
@ViewChild('myForm') myForm: NgForm;
// the data of ngModelGroup is saved in myForm
this.myForm.form.patchValue({
userData: {
// data of ngModelGroup
},
email: 'my@email.com',
// other form fields
});
Use value of Form
All the data in ngForm
is in ngForm.value
.
Reset form
reset the form concludes:
- clear the data
- clear the states, like valid, touched, etc
@ViewChild('myForm') myForm: NgForm;
this.myForm.reset()
Bonus: Two way binding
A very useful method:
<input #email="ngModel"
name="email"
[(ngModel)]="email"
email required/>
export class AComponent {
email = "";
}
Reactive
Set up
use formGroup
in @angular/forms
.
<form [formGroup]="signupForm">
<input formControlName="username">
<input formControlName="email">
</form>
this.signupForm = new FormGroup({
'username': new FormControl(null ),
'email': new FormControl(null)
});
To submit form, just bind the onSubmit()
to ngSubmit
.
<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
</form>
onSubmit(){
// do something with this.signupForm
}
Validation
In FormGroup
:
{
'username': new FormControl(null, Validators.required),
'email': new FormControl(null, [Validators.required, Validators.email]),
}
To show error message:
<input formControlName="email">
<span *ngIf="signupForm.get('email').invalid && signupForm.get('email').touched">Email error</span>
To add the validation to dynamic form field, use formArray
:
<div formArrayName="newFields">
<button (onClick)="addNewFieldCtrl()"></button>
<div *ngFor="let newFormCtrl of getNewFormCtrl(); let i = index">
<input [formControlName]="i">
</div>
</div>
addNewFieldCtrl() {
const ctrl = new FormControl(null, Validators.required);
(this.signupForm.get('newFields') as FormArray).push(ctrl);
}
getNewFormCtrl() {
return (this.signupForm.get('newFields') as FormArray).controls;
}
To create custom validator:
forbiddenNames = ['admin', 'user'];
forbiddenName(ctrl: FormControl): {[s: string]: boolean} {
if (this.forbiddenNames.includes(ctrl.value)) {
return {'isForbiddenName': true}
}
return null;
}
{
'username': new FormControl(
null,
[Validators.required, this.forbiddenName.bind(this)])
}
isForbiddenName
can be accessed by this.signupForm.get('username').errors['isForbiddenName']
.
There are two Observables: valueChanges
and stateChanges
. They listen to the changes in form
.
this.signupForm.valueChanges.subscribe(
value => console.log(value )
);