Angular Template-Driven Forms

By Arvind Rai, July 11, 2019
Angular template-driven form is the form where we write controls, validations and logics in HTML template. Angular has two approach of creating forms i.e. reactive form and template-driven form. In reactive form, the logic of creating forms and validations are handled in component class whereas in template-driven form the logic of creating forms and validations are handled in HTML template. In reactive form, validation is handled by functions in component class whereas in template-driven form validation is handled by directives. We should use template-driven form where small form is needed and no boilerplate code of logic is required. Angular provides NgForm and NgModel directive to create template-driven form. Here on this page we will create a template-driven form with text box, single select and multiple select option, radio button and checkbox.

Technologies Used

Find the technologies being used in our example.
1. Angular 8.0.3
2. TypeScript 3.4.3
3. Node.js 12.5.0
4. Angular CLI 8.0.6

1. Import FormsModule

To create Template-driven form, we need to import FormsModule in our application module. FormsModule provides NgForm and NgModel directive that is used to create template-driven form.
app.module.ts
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [  
        ------   
        FormsModule
  ],
  ------
})
export class AppModule { } 

2. NgForm

Template-driven form is created using NgForm that creates top-level FormGroup instance and binds it to a form to track aggregate form value and validation status. FormGroup tracks the value and validity state of a group of form control. To use NgForm we need to import FormsModule in application module. As soon as we import FormsModule in application module, the NgForm will be active in all form tags. In case we don’t want to make NgForm directive active in form tag by default then add ngNoForm in the form tag.
Exporting NgForm directive into a local template variable is optional but it is useful to access aggregate value and validity status of the form. NgForm is exported in local template as #userForm="ngForm".
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)">
------
</form> 
Using local template variable userForm, we can access form in our HTML template as following.
userForm.value: It returns form value. It means we get an object containing the values of all the fields used within <form> tag.
userForm.valid: It returns boolean value. When the value is true then form is valid. Form is said to be valid if all the fields returns validity true.
userForm.touched: It returns boolean value. If the value is true, it means user has entered value at least in one field.
userForm.submitted: It returns boolean value. If the value is true, it means form has been submitted.

3. NgModel

NgModel directive creates a FormControl instance from a domain model created in class and binds it to a form control element. FormControl keeps the track of user interaction and validation status of the form control. NgModel can be used standalone as well as with the parent form as template-driven form.
Find the code to use NgModel.
<input [(ngModel)]="userName" required #uname="ngModel">

<p>Name value: {{ userName }} </p>
<p>Name value: {{ uname.value }} </p>
<p>Name valid: {{ uname.valid }} </p> 
Now look into #uname="ngModel". Here uname is a local template variable that can be used to get form control state and validity with properties such as value, valid, invalid etc.
NgModel can be used as one-way binding and two-way binding. In one way binding we use [ ] sign and when value changes in domain model in the class then that it is set to view also. In two-way binding we use [( )] sign and the value changes in domain model is set to view and values changes in view is also set to domain model.

4. NgForm with NgModel

In template-driven form we need to use NgForm with NgModel directive. NgForm is used with form tag and NgModel is used with form elements such as text, radio button, checkbox, select box etc. When we use ngModel with form elements, we have also to supply a name attribute to register that control with parent form using that name.
When we are using parent form, we need not to use two-way binding to get form fields values in our class. We can pass <form> local template variable such as userForm to our submit method such as onFormSubmit(userForm) as given below.
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)"> 
On form submit, the form values can be accessed in class as given below.
onFormSubmit(userForm: NgForm) {
    console.log(userForm.value);
    console.log('Name:' + userForm.controls['name'].value);
} 
If we use neither one-way binding not two-way binding still we need to use ngModel attribute in fields when using with parent form as given below.
<input name="message" ngModel> 
If we will not use ngModel attribute in fields such as
<input name="message" > 
Then this field will not be registered with parent form and its value will not be passed to our class on form submit.

Let us understand case by case.
Case-1: Here we are using two-way binding in parent form.
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)">

  <input name="message" [(ngModel)] = "msg">

  <button>Submit</button>
</form> 
If we want form fields values using domain model then we can use two-way binding. On form submit we can also access form values using NgForm in our class. In case of parent form, most of the time, we need not to use two-way binding because we can access values in our class using NgForm itself. name attribute is required to register control with form.
Case-2: Here we are using one-way binding in parent form.
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)">

  <input name="message" [ngModel] = "msg">

  <button>Submit</button>
</form> 
We should use one-way binding in the scenario where we want to pre-populate form fields. name attribute is required to register control with form.
Case-3: Here we are using neither one-way nor two-way binding.
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)">

  <input name="message" ngModel>

  <button>Submit</button>
</form> 
To register child control with the form, we need to use ngModel as field attribute. If we are using neither one-way nor two way binding, still we need to use ngModel attribute so that the field can be registered with parent form otherwise the field value will not be passed on form submit. name attribute is required to register control with form.
Case-4: Here we are not using ngModel in any way.
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)">

  <input name="message">

  <button>Submit</button>
</form> 
As we know that to register child control with the form, we need to use ngModel as field attribute. If we are not using ngModel as field attribute then that field will not be registered with parent form and hence the field value will not be passed on form submit to our class.

5. Submit Form

We use local template variable of NgForm to submit form data with ngSubmit. Look into HTML form code.
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)">
------
</form> 
We have added following code in our form tag.
(ngSubmit)="onFormSubmit(userForm)" 
Here when form is submitted, local template variable is passed to our onFormSubmit method.
onFormSubmit(userForm: NgForm) {
    console.log(userForm.value);
    console.log('Username:' + userForm.controls['userName'].value);
    console.log('Form Valid:' + userForm.valid);
    console.log('Form Submitted:' + userForm.submitted);
}
Using controls[], we can access the form field value by field name in our class as following.
userForm.controls['userName'].value;
userForm.controls['age'].value;
userForm.controls['gender'].value 
Here userName, age, gender are the form fields names.
If we have a user class with fields names same as form control names then we can get form value as user class object. Suppose we have a user class as following.
export class User {
    userName: string;
    age: number;
    gender: string;
} 
We can get form vaues after submit as User object as following.
onFormSubmit(form: NgForm) {
    let newUser: User = form.value;
    console.log("User Name: " + user.userName);
} 

6. Reset Form

To reset template-driven form, NgForm provides resetForm() method that is called as following.
resetUserForm(userForm: NgForm) {
     userForm.resetForm();;
} 
To call the above function, create a button in UI.
<button type="button" (click)="resetUserForm(userForm)">Reset</button> 
If we want to reset form with some default values, then assign the default values with form control name of the form.
resetUserForm(userForm: NgForm) {
   userForm.resetForm({
      userName: 'Mahesh',
      age: 20
   });
} 

7. Validations

To use Angular validations we need to disable HTML 5 validation in our form using novalidate attribute. Find the text box with Angular required and minlength validations.
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)" novalidate>
	<input name="userName" required minlength="5" ngModel #userName="ngModel">
	<div *ngIf="userName.errors" class="error">
		<div *ngIf="userName.errors.required">
			Username required.
		</div>
		<div *ngIf="userName.errors.minlength">
			Minimum length 5 characters.
		</div>
	</div>
------
</form> 
To get form control object, we need to export ngModel into local template variable as #userName="ngModel". Now using userName we can get form control value, validity status and error object. Error object is obtained as userName.errors and to get particular validation such as required and minlength, we write code as userName.errors.required and userName.errors.minlength to display error message accordingly.

8. Set Value Dynamically

To set value in template-driven form dynamically, NgForm provides setValue method. Suppose we have following class whose field names are matching with our template-driven form elements names.
user.ts
export class User {
    userName: string;
    age: number;
    gender: string;
    isMarried: boolean;
    isTCAccepted: boolean;
 
We can set values to the form view dynamically as following.
setDefaultValues(form: NgForm) {
	let user = new User();
	user.userName = "Narendra Modi";
	user.age = 20;
	user.gender = 'male';
	user.isMarried = false;
	user.isTCAccepted = false;
	
	form.setValue(user);
} 
setValue sets value to each and every form elements of the given form and if we miss any form element name in setValue, it will throw error.

9. Complete Example

Find the project structure of our demo application.
angular-demo
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--domain
|   |   |    | 
|   |   |    |--profile.ts
|   |   |    |--technology.ts
|   |   |    |--user.ts
|   |   |
|   |   |--services
|   |   |    | 
|   |   |    |--user-service.ts
|   |   |
|   |   |--user-component
|   |   |    | 
|   |   |    |--user.component.ts
|   |   |    |--user.component.html
|   |   |    |--user.component.css
|   |   |
|   |   |--app.component.ts
|   |   |--app.module.ts 
|   | 
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json 
Now find the complete code.
profile.ts
export class Profile { 
    constructor(public prId:string, public prName:string) {
    }	
} 
technology.ts
export class Technology { 
    constructor(public techId:number, public techName:string) {
    }
} 
user.ts
import { Profile } from './profile';
import { Technology } from './technology';

export class User {
    userName: string;
    age: number;
    gender: string;
    isMarried: boolean;
    isTCAccepted: boolean;
    profile: Profile;
    technologies: Technology[];
} 
user-service.ts
import { Injectable } from '@angular/core';

import { Profile } from '../domain/profile';
import { Technology } from '../domain/technology';
import { User } from '../domain/user';

@Injectable()
export class UserService {
    getPofiles(): Profile[] {
        let profiles = [
            new Profile('dev', 'Developer'),
            new Profile('man', 'Manager'),
            new Profile('dir', 'Director')
        ]
        return profiles;
    }
    getTechnologies(): Technology[] {
        let technologies = [
            new Technology(100, 'Java'),
            new Technology(101, 'Angular'),
            new Technology(102, 'Hibernate'),
            new Technology(103, 'Spring')
        ]
        return technologies;
    }
    createUser(user: User) {
        //Log user data in console
        console.log("User Name: " + user.userName);
        console.log("User age: " + user.age);
        console.log("Profile Id: " + user.profile.prId);
        console.log("Profile Name: " + user.profile.prName);
        for (let i = 0; i < user.technologies.length; i++) {
            console.log("Technology Id: " + user.technologies[i].techId);
            console.log("Technology Name: " + user.technologies[i].techName);
        }
        console.log("Gender: " + user.gender);
        console.log("Married: " + user.isMarried);
        console.log("T & C Accepted: " + user.isTCAccepted);
    }
} 
user.component.ts
import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';

import { UserService } from '../services/user-service';
import { Profile } from '../domain/Profile';
import { Technology } from '../domain/technology';
import { User } from '../domain/user';

@Component({
	selector: 'app-template',
	templateUrl: './user.component.html',
	styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
	isValidFormSubmitted = false;
	allProfiles: Profile[];
	allTechnologies: Technology[];
	initialProfileVal = null;
	constructor(private userService: UserService) { }
	ngOnInit(): void {
		this.allProfiles = this.userService.getPofiles();
		this.allTechnologies = this.userService.getTechnologies();
	}
	onFormSubmit(form: NgForm) {
		this.isValidFormSubmitted = false;
		if (form.valid) {
			this.isValidFormSubmitted = true;
		} else {
			return;
		}
		let newUser: User = form.value;
		this.userService.createUser(newUser);
		this.resetUserForm(form);
	}
	resetUserForm(form: NgForm) {
		form.resetForm();
		this.initialProfileVal = null;
	}
	setDefaultValues(form: NgForm) {
		let user = new User();
		user.userName = "Narendra Modi";
		user.age = 20;
		user.gender = 'male';
		user.isMarried = false;
		user.profile = this.allProfiles[2];
		user.technologies = [
			this.allTechnologies[1],
			this.allTechnologies[3]
		];
		user.isTCAccepted = false;
		form.setValue(user);
	}
	compareTech(t1: Technology, t2: Technology): boolean {
		//console.log(t1.techId +'-' + t2.techId);
		return t1 && t2 ? t1.techId === t2.techId : t1 === t2;
	}
} 
user.component.html
<h3>User Information</h3>
<p *ngIf="isValidFormSubmitted" [ngClass]="'success'">
	Form submitted successfully.
</p>
<form #userForm="ngForm" (ngSubmit)="onFormSubmit(userForm)" novalidate>
	<table>
		<tr>
			<td>Username:</td>
			<td>
				<input name="userName" required minlength="5" ngModel #userName="ngModel">
				<div *ngIf="userName.errors && userForm.submitted && !isValidFormSubmitted" class="error">
					<div *ngIf="userName.errors.required">
						Username required.
					</div>
					<div *ngIf="userName.errors.minlength">
						Minimum length 5 characters.
					</div>
				</div>
			</td>
		</tr>
		<tr>
			<td>Age:</td>
			<td>
				<input type="number" name="age" ngModel required min="18" #age="ngModel">
				<div *ngIf="age.errors && userForm.submitted && !isValidFormSubmitted" class="error">
					<div *ngIf="age.errors.required">
						Age required.
					</div>
				</div>
			</td>
		</tr>
		<tr>
			<td>Select Profile: </td>
			<td>
				<select name="profile" [ngModel]="initialProfileVal" ngModel required #profile="ngModel">
					<option [ngValue]="null">Choose your profile</option>
					<option *ngFor="let prf of allProfiles" [ngValue]="prf">
						{{ prf.prName }}
					</option>
				</select>
				<div *ngIf="profile.errors && userForm.submitted && !isValidFormSubmitted" class="error">
					<div *ngIf="profile.errors.required">
						Profile required.
					</div>
				</div>
			</td>
		</tr>
		<tr>
			<td>Select Technologies: </td>
			<td>
				<select multiple name="technologies" [compareWith]="compareTech" ngModel required #selectedTechs="ngModel">
					<option *ngFor="let tech of allTechnologies" [ngValue]="tech">
						{{ tech.techName }}
					</option>
				</select>
				<div *ngIf="selectedTechs.errors && userForm.submitted && !isValidFormSubmitted" class="error">
					<div *ngIf="selectedTechs.errors.required">
						Technologies required.
					</div>
				</div>
			</td>
		</tr>
		<tr>
			<td>Gender:</td>
			<td>
				<input type="radio" value="male" name="gender" ngModel required #gender="ngModel"> Male
				<input type="radio" value="female" name="gender" ngModel required #gender="ngModel"> Female
				<div *ngIf="gender.errors && userForm.submitted && !isValidFormSubmitted" class="error">
					<div *ngIf="gender.errors.required">
						Gender required.
					</div>
				</div>
			</td>
		</tr>
		<tr>
			<td>Are you married? </td>
			<td>
				<input type="checkbox" name="isMarried" ngModel>
			</td>
		</tr>
		<tr>
			<td>Accept T & C </td>
			<td>
				<input type="checkbox" name="isTCAccepted" ngModel required #tc="ngModel">
				<div *ngIf="tc.invalid && userForm.submitted && !isValidFormSubmitted" class="error">
					Accept T & C.
				</div>
			</td>
		</tr>
		<tr>
			<td colspan="2">
				<button>Submit</button>
				<button type="button" (click)="setDefaultValues(userForm)">Set Default</button>
				<button type="reset" (click)="resetUserForm(userForm)">Reset</button>
			</td>
		</tr>
	</table>
</form> 
user.component.css
table {
    border-collapse: collapse;
}
table, th, td {
    border: 1px solid black;
}
.error {
    color: red;
}
.success {
    color: green;
} 
app.component.ts
import { Component } from '@angular/core';

@Component({
   selector: 'app-root',
   template: `
		<app-template></app-template>
             `
})
export class AppComponent { 
} 
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { UserComponent } from './user-component/user.component';
import { UserService } from './services/user-service';

@NgModule({
      imports: [
            BrowserModule,
            FormsModule
      ],
      declarations: [
            AppComponent,
            UserComponent
      ],
      providers: [
            UserService
      ],
      bootstrap: [
            AppComponent
      ]
})
export class AppModule { } 

10. Run Application

To run the application, find the steps.
1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200
Find the print screen.
Angular Template-Driven Forms

References

Template-driven forms
Angular NgForm with NgModel Directive Example

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us