💡 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
- Module System in Angular
Happy Coding!
Top comments (2)
Hi Renuka Patil,
Very nice and helpful !
Thanks for sharing.
Thank you João!