💡 There are two approaches to handle user’s input
- Template- driven form → html template
- Reactive forms ( Model driven form) → component
1.Template-driven form
- If you want to add simple form (login, contact , signup)
- Template-driven forms not scalable, expandable, upgradable
- Very basic logic
- Easily managed in html template
- Can be created using directives → ngForm, ngModel
- Basic validation can be used to validate form → required, maxlength, pattern
- Custom validation , directives can be used
- Less-explicit (automate)
- Works *Asynchronously *→ page does not reload
- Two-way data binding used
2.Reactive forms
- More robust
- Created in component class
- Scalable, reusable, testable
- Most prefferd to use if forms are key part of your application
- More explicit(manual work)
- Synchronous
- Code-driven
- Eliminate the anti-pattern of updating the data model via Two-Way data binding
1.Template-driven form
import FormsModule
in app.module.ts
@NgModule({
imports: [
FormsModule
],
})
Html file:
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="bg-primary text-center p-3">
<h2>Template driven form</h2>
</div><br><br>
<form #newForm="ngForm" (ngSubmit)="save(newForm.value)" >
<div class="form-group">
<label for="">Enter Name: </label> <br>
<input type="text" name="name" ngModel class="form-control" placeholder="Enter Name">
</div><br>
<div class="form-group">
<label for="">Enter Age: </label> <br>
<input type="number" name="age" ngModel class="form-control" placeholder="Enter Age">
</div><br>
<div class="form-group">
<label for="">Enter email: </label> <br>
<input type="email" name="email" ngModel class="form-control" placeholder="Enter Email" >
</div><br>
<div class="d-grid">
<input type="submit" value="submit" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
note: ngModel → two way data binding
newForm → template reference variable
component file
save(formData: any){
console.log(formData);
}
Output:
Lets try to use json pipe and show the results in html template file: {{ newForm.value | json}}
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="bg-primary text-center p-3">
<h2>Template driven form</h2>
</div><br><br>
<form #newForm="ngForm" (ngSubmit)="save(newForm.value)" >
{{ newForm.value | json}}
<div class="form-group">
<label for="">Enter Name: </label> <br>
<input type="text" name="name" ngModel class="form-control" placeholder="Enter Name">
</div><br>
<div class="form-group">
<label for="">Enter Age: </label> <br>
<input type="number" name="age" ngModel class="form-control" placeholder="Enter Age">
</div><br>
<div class="form-group">
<label for="">Enter email: </label> <br>
<input type="email" name="email" ngModel class="form-control" placeholder="Enter Email" >
</div><br>
<div class="d-grid">
<input type="submit" value="submit" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
Binding form data to model - two way data binding
student.ts→ model class
export class Student {
constructor(
public name?: string,
public age?: number,
public email?: string
) {}
}
note: ?
used to make nullable
component file
save(formData: any){
const std = new Student(formData.name, formData.age, formData.email);
}
two-way data binding → [(ngModel)]
Html file
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="bg-primary text-center p-3">
<h2>Template driven form</h2>
</div><br><br>
<form #newForm="ngForm" (ngSubmit)="save(newForm.value)" >
{{ newForm.value | json}}
<div class="form-group">
<label for="">Enter Name: </label> <br>
<input type="text" name="name" [(ngModel)]="std.name" class="form-control" placeholder="Enter Name" >
</div><br>
<div class="form-group">
<label for="">Enter Age: </label> <br>
<input type="number" name="age" [(ngModel)]="std.age" class="form-control" placeholder="Enter Age" >
</div><br>
<div class="form-group">
<label for="">Enter email: </label> <br>
<input type="email" name="email" [(ngModel)]="std.email" class="form-control" placeholder="Enter Email" >
</div><br>
<div class="d-grid">
<input type="submit" value="submit" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
Component file
std = new Student("", 0, "");
save(formData: any){
console.log(this.std);
}
Form validation
- Provided by validation directives
- In built validators
- Novalidate attribute on element → to disable browser validation
- Useful properties: touched untouched valid invalid dirty pristine
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="bg-primary text-center p-3">
<h2>Template driven form</h2>
</div><br><br>
<form #newForm="ngForm" (ngSubmit)="save(newForm.value)" novalidate>
{{ newForm.value | json}}
<div class="form-group">
<label for="">Enter Name: </label> <br>
<input type="text" required #name="ngModel" name="name" [(ngModel)]="std.name" class="form-control" placeholder="Enter Name" >
</div><br>
touched: {{name.touched}}
untouched: {{name.untouched}}
<br>
valid: {{name.valid}}
invalid: {{name.invalid}}
<br>
pristin: {{name.pristine}}
dirty: {{name.dirty}}
<br>
<div class="form-group">
<label for="">Enter Age: </label> <br>
<input type="number" name="age" [(ngModel)]="std.age" class="form-control" placeholder="Enter Age" >
</div><br>
<div class="form-group">
<label for="">Enter email: </label> <br>
<input type="email" name="email" [(ngModel)]="std.email" class="form-control" placeholder="Enter Email" >
</div><br>
<div class="d-grid">
<input type="submit" value="submit" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
Let's put validations on each field, validations only shows when field is touched and kept empty which violates our validations
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="bg-primary text-center p-3">
<h2>Template driven form</h2>
</div><br><br>
<form #newForm="ngForm" (ngSubmit)="save(newForm.value)" novalidate>
<div class="form-group">
<label for="">Enter Name: </label> <br>
<input type="text" minlength="3" maxlength="15" required #name="ngModel" name="name" [class.is-invalid]="name.touched && name.invalid" [(ngModel)]="std.name" class="form-control" placeholder="Enter Name" >
</div><br>
<div class="alert alert-danger" *ngIf="name.touched && name.invalid">
<div *ngIf="name.errors && name.errors['required']">
name is required
</div>
<div *ngIf="name.errors && name.errors['minlength']">
3 characters must
</div>
</div>
<div class="form-group">
<label for="">Enter Age: </label> <br>
<input type="number" min="10" max="50" #age="ngModel" [class.is-invalid]="age.touched && age.invalid" name="age" [(ngModel)]="std.age" class="form-control" placeholder="Enter Age" >
</div><br>
<div class="alert alert-danger" *ngIf="age.touched && age.invalid">
<div *ngIf="age.errors && age.errors['required']">
age is required
</div>
<div *ngIf="email.errors && email.errors['min']">
age must at least 10
</div>
<div *ngIf="email.errors && email.errors['max']">
age must at most 50
</div>
</div>
<div class="form-group">
<label for="">Enter email: </label> <br>
<input type="email" pattern="/^[a-zA-Z0-9. _-]+@[a-zA-Z0-9. -]+\. [a-zA-Z]{2,4}$/ " required #email="ngModel" [class.is-invalid]="email.touched && email.invalid" name="email" [(ngModel)]="std.email" class="form-control" placeholder="Enter Email" >
</div><br>
<div class="alert alert-danger" *ngIf="email.touched && email.invalid">
<div *ngIf="email.errors && email.errors['required']">
email is required
</div>
<div *ngIf="email.errors && email.errors['pattern']">
email is required
</div>
</div>
<div class="d-grid">
<input type="submit" [class.disabled]="newForm.form.invalid" value="submit" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
Radio buttons and Dropdownlist
export class Student {
constructor(
public name?: string,
public age?: number,
public email?: string,
public gender?: string,
public country?: string
) {
}
}
std = new Student("", 0, "");
constructor() {
this.std.country = "";
}
save(formData: any){
console.log(this.std);
}
//radio
<label for="">Gender</label>
<div class="form-check">
<input type="radio" required name="gender" #gender="ngModel" ngModel [(ngModel)]="std.gender" value="Male" class="form-check-input">
<label for="">Male</label>
</div>
<div class="form-check">
<input type="radio" required name="gender" #gender="ngModel" ngModel [(ngModel)]="std.gender" value="Female" class="form-check-input">
<label for="">Female</label>
</div>
//dropdown
<div class="form-group">
<label for="">Country</label>
<select name="country" class="form-control" required="" [class.is-invalid]="country.touched && country.invalid" #country="ngModel" ngModel [(ngModel)]="std.country" id="">
<option value="" selected>Select</option>
<option value="USA">USA</option>
<option value="Canada">Canada</option>
<option value="India">India</option>
</select>
</div>
<div class="alert alert-danger" *ngIf="country.touched && country.invalid">
<div *ngIf="country.errors && country.errors['required']">
country is required
</div>
</div>
Checkboxes
constructor(
public name?: string,
public age?: number,
public email?: string,
public gender?: string,
public country?: string,
public agree?: boolean,
public hobby?: string
) { }
//single-select
<div class="form-check">
<input type="checkbox" required name="agree" #agree="ngModel" ngModel [(ngModel)]="std.agree" class="form-check-input">
<label for="">I accept terms and conditions</label>
</div>
<br>
<label for="">Select Hobbies: </label>
//multi-select
<div class="form-check">
<input type="checkbox" name="Hobbies" ngModel (change)="onChange($event)" value="Cricket" class="form-check-input">
<label for="">Cricket</label>
</div>
<div class="form-check">
<input type="checkbox" name="Hobbies" ngModel (change)="onChange($event)" value="Reading" class="form-check-input">
<label for="">Reading</label>
</div>
<div class="form-check">
<input type="checkbox" name="Hobbies" ngModel (change)="onChange($event)" value="singing" class="form-check-input">
<label for="">singing</label>
</div>
<br>
<div class="d-grid">
<input type="submit" [class.disabled]="newForm.form.invalid || selectedHobbies.length == 0" value="submit" class="btn btn-primary">
</div>
component file
selectedHobbies: string[]=[];
std = new Student("", 0, "");
constructor() {
this.std.country = "";
}
save(formData: any){
console.log(this.std);
console.log(this.selectedHobbies);
}
onChange(event: any){
let selected = event.target.value;
let checked = event.target.checked;
if(checked){
this.selectedHobbies.push(selected);
}else{
let index = this.selectedHobbies.indexOf(selected);
this.selectedHobbies.splice(index, 1);
}
}
Reset Form
<button type="button" (click)="resetForm(newForm)" class="btn btn-danger">Reset</button>
import {NgForm} from '@angular/forms'
resetForm(formData: NgForm){
formData.reset();
this.selectedHobbies = [];
}
save(formData: any){
console.log(this.std);
console.log(this.selectedHobbies);
//formData.reset();
}
2. Reactive forms
- Introduced in ANGULAR2
- model-driven forms
- no need of two way binding
- form-group, form controls and form arrays
- we also define validation rules
- then we bind to html form
- template → logic and controls defined in html form
advantages:
- using custom validators
- changing validation dynamically
- dynamically adding form feilds
Import ReactiveFormsModule in app.module.ts
import { ReactiveFormsModule } from '@angular/forms';
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="text-primary text-center">
<h3>Reactive Form</h3>
</div>
<form [formGroup]="signupForm" (ngSubmit)="save()" action="">
<div class="form-group">
<label for="">Enter Name</label>
<input type="text" formControlName="name" name="name" class="form-control">
</div>
<div class="form-group">
<label for="">Enter Age</label>
<input type="text" formControlName="age" name="age" class="form-control">
</div>
<div class="form-group">
<label for="">Enter Email</label>
<input type="text" formControlName="email" name="email" class="form-control">
</div>
<br>
<div class="d-grid">
<input type="submit" value="Submit" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
signupForm = new FormGroup({
name: new FormControl(''),
age: new FormControl(''),
email: new FormControl(''),
});
save(){
console.log(this.signupForm.value);
}
Form Validation
Getter function is only in reactive forms.
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="text-primary text-center">
<h3>Reactive Form</h3>
</div>
<form [formGroup]="signupForm" (ngSubmit)="save()" action="">
<div class="form-group">
<label for="">Enter Name</label>
<input type="text" formControlName="name" [class.is-invalid]="f['name'].invalid && f['name'].touched" name="name" class="form-control">
</div>
<div class="alert alert-danger" *ngIf="f['name'].invalid && f['name'].touched">
<div *ngIf="f['name'].errors && f['name'].errors['required']">
Name is required
</div>
<div *ngIf="f['name'].errors && f['name'].errors['minlength']">
3 characters minimum
</div>
</div>
<div class="form-group">
<label for="">Enter Age</label>
<input type="text" formControlName="age" [class.is-invalid]="f['age'].invalid && f['age'].touched" name="age" class="form-control">
</div>
<div class="alert alert-danger" *ngIf="f['age'].invalid && f['age'].touched">
<div *ngIf="f['age'].errors && f['age'].errors['required']">
age is required
</div>
<div *ngIf="f['age'].errors && f['age'].errors['min']">
age at least 10
</div>
<div *ngIf="f['age'].errors && f['age'].errors['max']">
age at most 50
</div>
</div>
<div class="form-group">
<label for="">Enter Email</label>
<input type="text" formControlName="email" [class.is-invalid]="f['email'].invalid && f['email'].touched" name="email" class="form-control">
</div>
<div class="alert alert-danger" *ngIf="f['email'].invalid && f['email'].touched">
<div *ngIf="f['email'].errors && f['email'].errors['required']">
email is required
</div>
<div *ngIf="f['email'].errors && f['email'].errors['email']">
invalid email
</div>
</div>
<br>
<div class="d-grid">
<input type="submit" value="Submit" [class.disabled]="signupForm.invalid" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
signupForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(3)]),
age: new FormControl('', [Validators.required, Validators.min(10), Validators.max(50)]),
email: new FormControl('', [Validators.required, Validators.email]),
});
save(){
console.log(this.signupForm.value);
}
get f(){
return this.signupForm.controls;
}
Radion button and dropdown
component file
signupForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(3)]),
age: new FormControl('', [Validators.required, Validators.min(10), Validators.max(50)]),
email: new FormControl('', [Validators.required, Validators.email]),
gender: new FormControl('', [Validators.required]),
country: new FormControl('', [Validators.required]),
});
html file
<label for="">Gender: </label>
<div class="form-check">
<input type="radio" name="gender" value="Male" formControlName="gender" class="form-check-input" id="">
<label for="">Male</label>
</div>
<div class="form-check">
<input type="radio" name="gender" value="Female" formControlName="gender" class="form-check-input" id="">
<label for="">Female</label>
</div>
<div class="form-group">
<label for="">Country:</label>
<select name="country" formControlName="country" [class.is-invalid]="f['country'].invalid && f['country'].touched" class="form-control" id="">
<option value="">Select</option>
<option value="India">India</option>
<option value="UK">UK</option>
<option value="USA">USA</option>
</select>
</div>
<div class="alert alert-danger" *ngIf="f['country'].invalid && f['country'].touched">
<div *ngIf="f['country'].errors && f['country'].errors['required']">
country is required
</div>
</div>
After filling the form:
Checkbox
hobbies: string[] = ['reading', 'writing', 'singing'];
signupForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(3)]),
age: new FormControl('', [Validators.required, Validators.min(10), Validators.max(50)]),
email: new FormControl('', [Validators.required, Validators.email]),
gender: new FormControl('', [Validators.required]),
country: new FormControl('', [Validators.required]),
agree: new FormControl(false, [Validators.requiredTrue]),
hobby: new FormArray([], [Validators.required]),
});
onChange(e:any){
const check = e.target.value;
const checked = e.target.checked;
const checkedArray = this.signupForm.get('hobby') as FormArray;
if(checked){
checkedArray.push(new FormControl(check));
}else{
let i: number = 0;
checkedArray.controls.forEach((item) => {
if(item.value == check){
checkedArray.removeAt(i);
}
i++;
});
}
}
save(){
console.log(this.signupForm.value);
}
get f(){
return this.signupForm.controls;
}
<label for="">Hobbies</label>
<div *ngFor="let hobby of hobbies; let i =index" class="">
<label for="">
<input type="checkbox" (change)="onChange($event)" class="form-check-input" [value]="hobby" name="hobby" id="">
{{hobby}}
</label>
</div>
<div class="form-check">
<input type="checkbox" name="gender" value="Female" formControlName="agree" class="form-check-input" id="">
<label for="">I accept terms and conditions</label>
</div>
You can see these blogs to cover all angular concepts:
Beginner's Roadmap: Your Guide to Starting with Angular
- Core Angular Concepts
- Services and Dependency Injection
- Routing and Navigation
- Forms in Angular
- RxJS and Observables
- State Management
- Performance Optimization
You can see these blogs to cover all angular concepts:
Beginner's Roadmap: Your Guide to Starting with Angular
- Core Angular Concepts
- Services and Dependency Injection
- Routing and Navigation
- Forms in Angular
- RxJS and Observables
- State Management
- Performance Optimization
Happy Coding!
Top comments (1)
Hi Renuka Patil,
Very nice and helpful !
Thanks for sharing.