[Angular reactive forms] #angular
A quick reference guide on how to setup.
import ReactiveFormsModule
import { ReactiveFormsModule } from '@angular/forms';
@NgModule ({
...
imports: [
ReactiveFormsModule,
...
]
})
define a FormGroup
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
...
export FormComponent {
public userForm: FormGroup;
public
constructor(
private fb: FormBuilder
){
this.userForm = this.fb.group({
username: ['user1', Validators.required] //default value = 'user1', Optional validator = required
});
this.username = this.userForm.controls['username']; //optional. Allows easier reference in HTML template
}
public onSubmit(formValue: any) {
console.log('You submitted: ', formValue);
}
}
Create form and bind controls
<form [formGroup]="userForm"
(ngSubmit)="onSubmit(userForm.value)"
>
<label for="usernameElm">Username</label>
<input type="text"
id="usernameElm"
placeholder="enter username here"
[formControl]="userForm.controls['username']"
>
<!--
optionally you could bing the element to the Control property:
[formControl]="username"
-->
<button type="submit">Submit</button>
</form>
Checking whole form validity example
<button [disabled]="!userForm.valid"
type="submit>Submit</button>
Detecting errors in controls
<input [class.error]="!userForm.controls['username'].valid"
...
Handling specific validation errors
<div *ngIf="userForm.controls['username'].hasError('required')">
You must enter a username
</div>
Good practice: see if control is touched
<!-- we are using the reference variable for leaner code -->
<input [class.error]="!username.valid && username.touched"
<div *ngIf="username.hasError('required') && username.touched"
Errors from FormGroup level
You can check for errors of specific field even in FormGroup level:
console.log (userForm.hasError('required','username'); // FormGroup.hasError(errorKey, fieldKey)
Create a function that accepts FormControl
object as parameter and returns an error string map {errorKey: boolean}
if the error is valid.
Define Validator Example: Not allow 'admin' as username.
private reservedUsernames (control: FormControl): { [string]: boolean } {
if (control.value === 'admin') {
return {reserved: true};
}
}
Apply Validator
this.userForm = this.fb.group({
username: ['', Validators.compose(
[Validators.required, this.reservedUsernames]
)]
});
Check in HTML template
<div *ngIf="userForm.controls['username'].hasError('reserved')">
Username reserved and cannot be used.
</div>
There is an available Observable valueChanges
in both FormGroup and FormControl classes.
this.userForm.valueChanges.subscribe(
(value: any) => console.log('Form Value changed to', value)
);
this.userForm.controls['usernames'].valueChanges.subscribe(
(value: string) => console.log('Current username: ', value);
);
If you need to... you can bind an input control value to an external variable.
Component class:
public currentUsername: string;
//example of setting the value of currentUsername
public suggest() {
this.currentUsername = 'user'+Math.floor((Math.random() * 10) + 1);
}
HTML template:
...
<!-- ngModel binds the input element value to variable
<input type="text"
placeholder="enter username here"
id="usernameElem"
[formControl]="userForm.controls['username']"
[(ngModel)]="currentUsername"
>
...
<!-- the currentUsername reflects the value of input element -->
<div> Current Value: {{currentUser}}</div>
<!-- setting the currentUsername reflects the value back to input element -->
<button (click)="suggest()">Suggest a username</button>
Note: ngModel
binds the input element value to variable. We still have to use [formControl]
to bind the input element with the rest of our FormGroup
and make it part of it (validation, etc).
[formGroup]
in a <form>
element, it does not automatically bind the ngForm
FormGroup.