Published: February 21 2023

Angular Template-Driven vs Reactive Forms

Below is a brief overview of the differences between Angular Reactive Forms and Template-Driven Forms followed by a simple example of each and how to choose between the two.

Angular Reactive Forms

Reactive forms use a model-driven approach where you define your form fields and validation rules in the component as a collection of controls in a FormGroup, and bind it to the form element in the template with a data-binding attribute (e.g. <form ... [formGroup]="form">).

To use Reactive Forms you need to import { ReactiveFormsModule } from '@angular/forms'; and add it to the imports array of the @NgModule decorator in your app module.


Angular Template-Driven Forms

With template-driven forms pretty much everything is defined in the template (as the name suggests). Form data is accessed via a template variable declared on the form element (e.g. <form #f="ngForm" ...>), the ngModel directive is used to register form input controls with the form (e.g. <input name="email" ngModel ... >), directives are used to specify validation rules on input elements (e.g. <input ... required email>), and model properties such as validation error messages (e.g. email.errors) are accessed via template variables declared on input elements (e.g. <input ... #email="ngModel">).

To use Template-Driven Forms you need to import { FormsModule } from '@angular/forms'; and add it to the imports array of the @NgModule decorator in your app module.


Reactive Forms Example

Below is a simple example form built with Reactive Forms, it contains a required field, a submit button and a reset button.

Angular Component with Reactive Forms

The app component contains an example form (FormGroup) created with the formBuilder.group() method, it contains a single form control for email. The control has a required validator and an email validator, the email validator tests that the control contains a valid email address.

The onSubmit() method simply displays the form data in a javascript alert when the form is valid.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({ selector: 'app-root', templateUrl: 'app.component.html' })
export class AppComponent implements OnInit {
    form!: FormGroup;
    submitted = false;

    constructor(private formBuilder: FormBuilder) { }

    ngOnInit() {
        this.form = this.formBuilder.group({
            email: ['', [Validators.required, Validators.email]]
        });
    }

    // convenience getter for easy access to form fields
    get f() { return this.form.controls; }

    onSubmit() {
        this.submitted = true;

        // stop here if form is invalid
        if (this.form.invalid) {
            return;
        }

        // display form values on success
        alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.form.value, null, 4));
    }

    onReset() {
        this.submitted = false;
        this.form.reset();
    }
}


Angular Component Template with Reactive Forms

The app component template contains the html markup for displaying the example email validation form in the browser. The form element uses the [formGroup] directive to bind to the form FormGroup in the app component above.

A specific validation message is displayed for each validator by checking the errors object for the input field (*ngIf="f.email.errors.required" and *ngIf="f.email.errors.email").

The form binds the form submit event to the onSubmit() handler in the app component using the Angular event binding (ngSubmit)="onSubmit()". Validation messages are displayed only after the user attempts to submit the form for the first time, this is controlled with the submitted property of the app component.

The reset button click event is bound to the onReset() handler in the app component using the Angular event binding (click)="onReset()".

CSS classes are from Bootstrap 5.

<form [formGroup]="form" (ngSubmit)="onSubmit()">
    <div class="row">
        <div class="col mb-3">
            <label class="form-label">Email</label>
            <input type="text" formControlName="email" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.email.errors }" />
            <div *ngIf="submitted && f.email.errors" class="invalid-feedback">
                <div *ngIf="f.email.errors.required">Email is required</div>
                <div *ngIf="f.email.errors.email">Email must be a valid email address</div>
            </div>
        </div>
    </div>
    <div class="text-center">
        <button class="btn btn-primary me-2">Submit</button>
        <button class="btn btn-secondary" type="reset" (click)="onReset()">Reset</button>
    </div>
</form>

See on StackBlitz at https://stackblitz.com/edit/angular-reactive-forms-email-validation


Template-Driven Forms Example

Below is a simple example form built with Template-Driven Forms, it contains a required field, a submit button and a reset button.

Angular Component with Template-Driven Forms

The component doesn't do much when using angular template-driven forms because form fields and validators are configured in the component template.

The onSubmit() method is called with the NgForm template variable when the form is submitted, it is bound to the form element in the template using Angular event binding syntax ((ngSubmit)="onSubmit(f)"). If the form is valid a simple javascript alert is displayed with the values entered.

import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({ selector: 'app-root', templateUrl: 'app.component.html' })
export class AppComponent {
    onSubmit(f: NgForm) {
        // stop here if form is invalid
        if (f.invalid) {
            return;
        }

        alert('SUCCESS!! :-)\n\n' + JSON.stringify(f.value, null, 4));
    }
}


Angular Component Template with Template-Driven Forms

The app component template contains an example form with a single input field for email. The field has the required and email validator directives, the email validator tests that the input field contains a valid email address.

The form submit event is bound to the onSubmit() method of the app component with the event binding (ngSubmit)="onSubmit(f)". The template variable #f="ngForm" creates a FormGroup instance to provide access to the form data and validation status in the app component.

Validators and directives in template-driven forms

Validation is implemented by validator functions that are attached to fields using directives in template-driven forms. The required and email validators are both included as part of the Angular framework.

Input fields registered with parent form

The email input field is registered with the form using the ngModel directive. In the context of a parent form the directive can be used without data binding ([()]), instead the control is registered using the input name attribute and the form keeps the data in sync.

CSS classes are from Bootstrap 5.

<form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
    <div class="row">
        <div class="col mb-3">
            <label class="form-label">Email</label>
            <input type="text" name="email" ngModel #email="ngModel" class="form-control" [ngClass]="{ 'is-invalid': f.submitted && email.invalid }" required email>
            <div *ngIf="f.submitted && email.errors" class="invalid-feedback">
                <div *ngIf="email.errors.required">Email is required</div>
                <div *ngIf="email.errors.email">Email must be a valid email address</div>
            </div>
        </div>
    </div>
    <div class="text-center">
        <button class="btn btn-primary me-1">Submit</button>
        <button class="btn btn-secondary" type="reset">Reset</button>
    </div>
</form>

See on StackBlitz at https://stackblitz.com/edit/angular-template-driven-forms-email-validation


Which should I choose?

It's generally recommended to use Reactive Forms for anything other than simple scenarios because there's a cleaner separation of concerns between form logic and HTML markup. Testing is easier with the form defined in code, and it handles more complex scenarios better, for example a large form with a lot of validators is more readable when it's separated from the HTML.

That being said I think it always comes down to specific requirements and preferences. Template-Driven Forms are easy to use (especially if you're coming from AngularJS), require a minimal amount of component code and can do pretty much everyhting Reactive Forms can.

My opinion: choose the approach that satisfies all your project requirements and that you're most comfortable with (or like the most).

Details on the official Angular recommendation (Reactive Forms) are avaiable at https://angular.io/guide/forms-overview#choosing-an-approach.

 


Need Some Angular Help?

Search fiverr for freelance Angular developers.


Follow me for updates

On Twitter or RSS.


When I'm not coding...

Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!


Comments


Supported by