Taner Şahin

TANER ŞAHİN

TANER ŞAHİN

profile-pic

Reactive and Template Driven Forms in Angular

1.01.0001

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>
      &nbsp;
      &nbsp;
      <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);
    }






#angular #softwaredevelopment #typescript #javascript

Share

Post Comments

Leave A Reply