Angular Forms
Angular is a very popular front-end framework created by Google. Just like other popular frameworks, it uses a component-based architecture. In this article, we’ll look at the basics of creating forms with Angular.
Different Kinds of Forms
Angular providing 2 different approaches to handling user input through forms.
They are reactive and template-driven forms. They both capture events from the view, validate their user input, and create a form model and data model to update, and provide a way to track changes.
Template Driven Forms
Easy to use
Suitable for simple scenarios
Fails for complex scenarios
Using two-way data binding
Lesser component code
Automatic track of the form and it’s data (Angular is handling them)
Unit testing is a challenge
Reactive Forms
More flexible but needs a lot of practice
Handles any kind of complex scenarios
More component code and less HTML markup
Unit testing is easy
Template-driven form with validation Angular example
Importing FormsModule
You need to import FormsModule into the AppModule (app.module.ts) for the Angular template-driven form to work. When you import the FormsModule in your component, Angular automatically creates and attaches a NgForm directive to the <form> tag in the template. Using NgForm you can track form value and validation status.
Add FormsModule in the imports section of the AppModule.
imports: [
BrowserModule,
FormsModule
],
Data Model (member.model.ts)
Member class defines the data model reflected in the form.
export class Member {
name: string;
mail: string;
membershipDate: Date;
}
Form Template (app.component.html)
In this Angular template driven form example we’ll have a form with two input fields for name and email, a date picker for picking date
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-8">
<h1>Membership Form</h1>
<form (ngSubmit)="onSubmit()" #membershipForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control"
id="name"
ngModel name="name" #name="ngModel" required>
<div class="alert alert-danger" *ngIf="name.invalid && name.touched">Name is required</div>
</div>
<div class="form-group">
<label for="email">email</label>
<input type="email" class="form-control"
id="email" required email
ngModel name="email" #email="ngModel">
<div class="alert alert-danger" *ngIf="email.invalid && email.touched">
Please enter a valid email
</div>
</div>
<div class="form-group">
<label for="mdate">Membership Date</label>
<input type="date" class="form-control" id="mdate"
[ngModel]="currentDate | date:'yyyy-MM-dd'" name="mdate">
</div>
<button type="submit" [disabled]="membershipForm.invalid" class="btn btn-success">Submit</button>
</form>
</div>
</div>
<hr>
<div *ngIf="submitted">
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-8">
<p>Name: {{member.name}}</p>
<p>email: {{member.mail}}</p>
<p>Membership Date: {{member.membershipDate | date:'dd/MM/yyyy'}}</p>
</div>
</div>
</div>
</div>
Important points to note
1) If you realized that ngModel is added with every element. It tells Angular that this element is a form of control. Just Membership date ngvalue Model is used with square brackets meaning as a one way binding to get the current date.
2) For name, email, and type HTML5 required attribute has been added to specify that a value is required. With email control, email attribute has also been added to specify that the entered value has to be a valid email. You can check the whole list of available Angular validators from here: https://angular.io/api/forms/Validators
3) Another important point is that the Angular framework doesn’t directly use HTML5 validator attributes. When it detects an HTML 5 validator attribute Angular adds a directive to that attribute. For example, if Angular detects a control marked with the email attribute, it adds its own EmailValidator directive.
4) Error message that is shown beneath the form controls is done by adding a <div> with the form control where message is needed. For example if you want to show error message for name control.
<div class="alert alert-danger" *ngIf="name.invalid && name.touched">Name is required</div>
Here alert and alert-danger are Bootstrap classes. Message is displayed if the control has been visited and name is not entered which is taken care of by *ngIf="name.invalid && name.touched".
5) Validation classes are added at the form level too apart from with each form control to determine whether the form as a whole is valid or not. Using that you can disable the submit button and enable it only when the whole form is valid. That’s what [disabled]="membershipForm.invalid" does in the submit button.
CSS class (app.component.css)
Also need to add a CSS class to show a bar on the left side of the control.
.ng-invalid.ng-untouched:not(form) {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid.ng-touched:not(form) {
border-left: 5px solid #a94442; /* red */
}
ng-invalid. ng-untouched: not(form) means select all elements having both ng-invalid and ng-untouched set within its class attribute and it is not at the form level. If you won’t add (form) you will have a bar at the form level too encompassing all controls.
Same way .ng-invalid.ng-touched: not(form) means select all elements having both ng-invalid and ng-touched set within its class attribute and it is not at form level.
Component class (app.component.ts)
import { NgForm } from '@angular/forms';
import { Member } from './member.model';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
@ViewChild('membershipForm') memberForm : NgForm;
currentDate = new Date();
member = new Member('', '', new Date(), '');
submitted = false;
onSubmit(){
this.submitted = true;
this.member.name = this.memberForm.value.name;
this.member.mail = this.memberForm.value.email;
this.member.membershipDate = this.memberForm.value.mdate;
} }
@ViewChild decorator is used here to access the local reference. Since local reference membershipForm holds a reference to the JS object for the form so this way you can get access to the form object in your typescript code.
Now you create a member object of the Member class where you will assign the field values when the onSubmit() is called.
Reactive Approach
Before start need to explain a few concepts
formGroup: The form will be treated as a FormGroup in the component class, so the formGroup directive allows to give a name to the form group.
ngSubmit: This is the event that will be triggered upon submission of the form.
formControlName: Each form field should have a formControlName directive with a value that will be the name used in the component class.
Example
A simple login form that using a reactive forms approach
app.component.ts
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
angForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.angForm = this.fb.group({
email: [''],
password: ['']
});
}
onClickSubmit(email, password) {
alert('Your Email is : ' + email);
}
}
app.component.html
<form [formGroup]="angForm">
<input type = "text"
name = "email"
placeholder = "email"
formControlName="email"
#email>
<br/>
<input type = "password"
name = "pwd"
placeholder = "password"
formControlName="password"
#password>
<br/>
<input type = "submit"
value = "submit"
(click) = "onClickSubmit(email.value, password.value)" >
</form>
How to create form dynamically using the Reactive Approach?
Example.html
<div [formGroup]="inviteForm">
<div formArrayName="items" *ngFor="let item of inviteForm.get('items')['controls']; let i = index;">
<div [formGroupName]="i">
<mat-form-field class="example-full-width" style="width:60%">
<mat-label>UserMailAdress</mat-label>
<input formControlName="mailAdress" matInput placeholder="EMail">
</mat-form-field>
<mat-form-field appearance="fill" style="width:35%">
<mat-label>UserType</mat-label>
<mat-select formControlName="userType">
<mat-option selected="selected" value="0" >Admin</mat-option>
<mat-option value="1">Agent' </mat-option>
<mat-option value="2">SuperUser</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
Example.ts
public inviteForm: FormGroup;
public items: FormArray;
public constructor(private formBuilder: FormBuilder) {
this.inviteForm = this.formBuilder.group({
items: this.formBuilder.array([this.createItem()])
});
}
public createItem(): FormGroup {
return this.formBuilder.group({
mailAdress: '',
userType: 0
});
}
public addItem(): void {
this.items = this.inviteForm.get('items') as FormArray;
this.items.push(this.createItem());
}
public removeItem(): void {
this.items = this.inviteForm.get('items') as FormArray;
const lastValue = this.items.length - 1;
this.items.removeAt(lastValue);
}
Post Comments