FormControl sets and tracks the individual HTML form element. it is one of the building blocks of the angular forms. The other two are FormGroup and FormArray. In this tutorial,we will learn what is FormControl is and learn some of the properties & methods of it.
Consider a simple text input box
First Name : <input type="text" name="firstname" />
As a developer, you would like to know the current value in the text box. You would also be like to know if the value is valid or not.. If the user has changed the value(dirty) or is it unchanged. You would like to be notified when the user changes its value.
The FormControl is an object that encapsulates all the information related to the single input element. It Tracks the value and validation status of each of these control.
The FormControl is just a class. A FormControl is created for each form field. We can refer to them in our component class and inspect its properties and methods
We can use FormControl to set the value of the Form field. Find the status of form field like (valid/invalid,pristine/dirty,touched/untouched ), etc. You can add validation rules to it.
The Angular has two approaches to building the Angular Forms. One is Template-driven and the other one is Reactive Forms.
To use the Angular forms, First, we need to import the FormsModule (for template-driven forms) & ReactiveFormsModule (for Reactive Forms) from the @angular/forms in your route module.
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
Also, add it to the imports metadata.
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
ReactiveFormsModule
],
In Reactive Forms approach, It is our responsibility to build the Model using FormGroup, FormControl and FormArray.
To use FormControl, first, we need to import the FormControl from the @angular/forms
import { FormGroup, FormControl, Validators } from '@angular/forms'
Then create the top-level FormGroup. The first argument to FormGroup is the collection of FormControl. They are added using the FormControl method as shown below.
reactiveForm = new FormGroup({
firstname: new FormControl('',[Validators.required]),
lastname: new FormControl(),
email: new FormControl(),
})
Or you can make use of the FormBuilder API
this.reactiveForm = this.formBuilder.group({
firstname: ['',[Validators.required]],
lastname: [''],
email: [''],
});
Bind the form element with the template using the formControlName directive as shown below
<form [formGroup]="reactiveForm" (ngSubmit)="onSubmit()" novalidate>
<p>
<label for="firstname">First Name </label>
<input type="text" id="firstname" name="firstname" formControlName="firstname">
</p>
<p>
<label for="lastname">Last Name </label>
<input type="text" id="lastname" name="lastname" formControlName="lastname">
</p>
<p>
<label for="email">Email </label>
<input type="text" id="email" name="email" formControlName="email">
</p>
<p>
<button type="submit">Submit</button>
</p>
</form>
In template-driven forms, the FormControl is defined in the Template. The <Form> directive creates the top-level FormGroup. We use the ngModel directive on each Form element, which automatically creates the FormControl instance.
<form #templateForm="ngForm" (ngSubmit)="onSubmit(templateForm)" novalidate>
<p>
<label for="firstname">First Name</label>
<input type="text" name="firstname" ngModel>
</p>
<p>
<label for="lastname">Last Name</label>
<input type="text" name="lastname" ngModel>
</p>
<p>
<label for="email">Email </label>
<input type="text" id="email" name="email" ngModel>
</p>
<p>
<button type="submit">Submit</button>
</p>
</form>
Use the viewChild to get the reference to the FormModel in the Component class. The control property of the NgForm returns the top-level formgroup
@ViewChild('templateForm',null) templateForm: NgForm;
abstract setValue(value: any, options?: Object): void
We use setValue or patchValue method of the FormControl to set a new value for the form control. There is no difference between setValue and patchValue at the FormControl level.
setEmail() {
this.reactiveForm.get("email").setValue("sachin.tendulakar@gmail.com");
};
setEmail() {
this.templateForm.control.get("email").setValue("sachin.tendulkar@gmail.com");
};
abstract patchValue(value: any, options?: Object): void
setEmail() {
this.reactiveForm.get("email").setValue("sachin.tendulakar@gmail.com");
};
setEmail() {
this.templateForm.control.get("email").setValue("sachin.tendulkar@gmail.com");
};
Must Read : setValue & patchValue in Angular forms
The two-way data binding is the preferred way to to keep the component model in sync with the FormModel in Template-driven forms.
<p>
<label for="firstname">First Name </label>
<input type="text" id="firstname" name="firstname" [(ngModel)]="contact.firstname">
</p>
Using two-way data binding in Reactive forms is deprecated since the Angular 7
value: any
The value returns the current value of FormControl It is Readonly. To set the value of the control either use the setValue or patchValue method
//reactive forms
this.reactiveForm.get("firstname").value
//template driven forms
this.templateForm.control.get("firstname").value
valueChanges: Observable <any>
The angular emits the valueChanges event whenever the value of the control changes. The value may change when the user updates the element in the UI or programmatically through the setValue/patchValue method. We can subscribe to it as shown below
//reactive Forms
this.fNameChange = this.reactiveForm.get("firstname").valueChanges.subscribe(x => {
console.log(x);
})
Similarly in template-driven forms.
setTimeout(() => {
this.fNameChange = this.templateForm.control.get("firstname").valueChanges.subscribe(x => {
console.log(x);
})
});
Must Read:ValueChanges in Angular
The FormControl tracks the validation status of the HTML Element to which it is bound. The following is the list of status-related properties
status: string
The Angular runs validation checks, whenever the value of a form control changes. Based on the result of the validation, the control can have four possible states.
VALID:
The FormControl has passed all validation checks.
INVALID:
This control has failed at least one validation check.
PENDING:
This control is in the midst of conducting a validation check.
DISABLED:
This control is exempt from validation checks
//reactive forms
this.reactiveForm.get("firstname").status
//template driven forms
this.templateForm.control.get("firstname").status
A control is valid when it has passed all the validation checks and is not disabled.
this.reactiveForm.get("firstname").valid
A control is invalid when it has failed one of the validation checks and is not disabled
this.reactiveForm.get("firstname").invalid
A control is pending when it is in the midst of conducting a validation check.
this.reactiveForm.get("firstname").pending
Control is disabled when its status is DISABLED.
this.reactiveForm.get("firstname").disabled
Control is enabled as long as the status is not DISABLED.
this.reactiveForm.get("firstname").disabled
Control is pristine if the user has not yet changed the value in the UI.
this.reactiveForm.get("firstname").pristine
Control is dirty if the user has changed the value in the UI.
this.reactiveForm.get("firstname").dirty
True if the control is marked as touched. A control is marked touched once the user has triggered a blur event on it.
this.reactiveForm.get("firstname").touched
True if the control has not been marked as touched. A control is untouched if the user has not yet triggered a blur event on it.
this.reactiveForm.get("firstname").untouched
We can also change the status of the control programmatically by using the following methods.
When we change the status of a control programmatically or via UI, the validity & value of the parent control is also calculated and updated. There may arise circumstances when you do not want that to happen. In such circumstances, you can make use of the onlySelf:true to ensure that the parent control is not checked.
This method will mark the control as touched.
markAsTouched(opts: { onlySelf?: boolean; } = {}): void
markAsDirty(opts: { onlySelf?: boolean; } = {}): void
Marks the control as dirty. A control becomes dirty when the control’s value is changed through the UI.
markAsPristine(opts: { onlySelf?: boolean; } = {}): void
Marks the control as pristine.
markAsPending(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void
Marks the control as pending. We mark it as pending when the control is in the midst of conducting a validation check.
disable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void
Disables the control. This means the control is exempt from validation checks and excluded from the aggregate value of any parent. Its status is DISABLED.
enable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void
Enables control. This means the control is included in validation checks and the aggregate value of its parent. Its status recalculates based on its value and its validators.
statusChanges: Observable <any>
We can subscribe to the status changes event by subscribing it to the statusChanges as shown below. The event is fired whenever the validation status of the control is calculated.
//Reactive Forms
this.reactiveForm.get("firstname").statusChanges.subscribe(x => {
console.log(x);
})
//Template Driven Forms
this.templateForm.control.get("firstname").statusChanges.subscribe(x => {
console.log(x);
})
Must Read:StatusChanges in Angular
The way we add validators depends on whether we use the Template-driven forms or reactive forms.
In Reactive forms, the validators are added while declaring the controls
reactiveForm = new FormGroup({
firstname: new FormControl('',[Validators.required]),
lastname: new FormControl(),
email: new FormControl(),
})
While in the template-driven forms in the template
<p>
<label for="firstname">First Name </label>
<input type="text" id="firstname" name="firstname" ngModel required >
</p>
updateValueAndValidity(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void
The updateValueAndValidity forces the form to perform validation. This is useful when you add/remove validators dynamically using setValidators, RemoveValidators etc
//reactive forms
this.reactiveForm.get("firstname").updateValueAndValidity();
//template driven forms
this.templateForm.control.get("firstname").updateValueAndValidity();
Programmatically adds the sync or async validators. This method will remove all the previously added sync or async validators.
setValidators(newValidator: ValidatorFn | ValidatorFn[]): void setAsyncValidators(newValidator: AsyncValidatorFn | AsyncValidatorFn[]): void
//Reactive Form
setValidator() {
this.reactiveForm.get("firstname").setValidators([Validators.required, Validators.minLength(5)]);
this.reactiveForm.get("firstname").updateValueAndValidity();
}
//Template driven forms
setValidator() {
this.templateForm.control.get("firstname").setValidators([Validators.required, Validators.minLength(5)]);
this.templateForm.control.get("firstname").updateValueAndValidity();
}
clearValidators(): void clearAsyncValidators(): void
clearValidators & clearAsyncValidators clears all validators.
//reactive forms
clearValidation() {
this.reactiveForm.get("firstname").clearValidators();
this.reactiveForm.get("firstname").updateValueAndValidity();
}
//template driven forms
clearValidation() {
this.templateForm.control.get("firstname").clearValidators();
this.templateForm.control.get("firstname").updateValueAndValidity();
}
errors: ValidationErrors | null
An object containing any errors generated by failing validation, or null if there are no errors.
getErrors() {
const controlErrors: ValidationErrors = this.reactiveForm.get("firstname").errors;
if (controlErrors) {
Object.keys(controlErrors).forEach(keyError => {
console.log("firtname "+ ' '+keyError);
});
}
}
setErrors(errors: ValidationErrors, opts: { emitEvent?: boolean; } = {}): void
setErrors() {
this.reactiveForm.get("firstname").setErrors( {customerror:'custom error'});
}
setErrors(errors: ValidationErrors, opts: { emitEvent?: boolean; } = {}): void
setErrors() {
this.reactiveForm.get("firstname").setErrors( {customerror:'custom error'});
}
getError(errorCode: string, path?: string | (string | number)[]): any
Reports error data for the control with the given path.
this.reactiveForm.getError("firstname")
//
this.reactiveForm.getError("address.pincode");
this.reactiveForm.getError(["address","pincode"]);
hasError(errorCode: string, path?: string | (string | number)[]): boolean
Reports whether the control with the given path has the error specified.
this.reactiveForm.hasError("firstname")
//
this.reactiveForm.hasError("address.pincode");
this.reactiveForm.hasError(["address","pincode"]);
abstract reset(value?: any, options?: Object): void
Resets the control. We can also pass the default value.
this.reactiveForm.get("firstname").reset('');
this.reactiveForm.get("firstname").reset('test');
In this tutorial, we learned what is FormControl is and looked at the various methods & properties that are available.
List of All tutorials on Angular Forms