DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Frameworks
  4. Custom Validators in Template Driven Angular 2 Forms

Custom Validators in Template Driven Angular 2 Forms

Angular 2 has two different kinds of Forms API, the reactive and template-driven approach. In this article, we will focus on the template driven approach and learn how to use it as well as how to build a custom validator with it.

Juri Strumpflohner user avatar by
Juri Strumpflohner
·
Nov. 24, 16 · Tutorial
Like (0)
Save
Tweet
Share
20.38K Views

Join the DZone community and get the full member experience.

Join For Free
Angular 2 has two different kinds of Forms API, the reactive and template-driven approach. In this article, we will focus on the template driven approach and learn how to use it as well as how to build a custom validator with it.

Todd Motto recently published a similar article on “Reactive FormGroup validation with AbstractControl in Angular 2”, which you might definitely want to check out for the reactive kind of approach to this.

Ok, before starting, let’s take a look at our simple Angular 2, template driven form.

<form #form="ngForm" (ngSubmit)="onSubmit(form.value)">
    <div>
        Firstname: 
        <input type="text" ngModel name="firstname" />
    </div>
    <div>
        <button>Submit</button>
        <button (click)="onReset($event, form)">reset</button>
    </div>
</form>

So far so good, binding works. For more details on how to setup your initial form binding, take a look at my article on that topic:

Adding Built-in Validators

We can make use of the built-in HTML5 validators in Angular 2 just as we were able already in Angular 1. Behind the scenes, Angular recognizes these validators and integrates with them.

That said, as a first step we add the novalidate attribute onto our form. This disables the browser validation behavior and lets us do the displaying of validation errors with Angular.

<form novalidate #form="ngForm" (ngSubmit)="onSubmit(form.value)">
    ...
</form>

Next, we can take a closer look at the input control and add a simple required attribute for making it a required input field.

<div>
    Firstname: 
    <input type="text" ngModel name="firstname" required />
</div>

This makes our input a required field and when it’s not compiled, the according form.valid property will be false. You may be wondering where the form comes from? It’s the template variable I’ve defined and associated with ngForm. This is the way it’s done in the Angular 2 template driven approach.

<form novalidate #form="ngForm" (ngSubmit)="form.valid && onSubmit(form.value)">
    ...
</form>

Note that I’m adding form.valid && onSubmit(...) to our (ngSubmit) event. This way our form won’t submit unless it is valid. Obviously we also need to display some kind of message. To do so, we first have to get a reference to our NgModel object that sits behind the <input>. This object is instantiated for us by Angular when we add the ngModel directive to our control. NgModel holds information about the input control such as validation errors.

<div>
    Firstname: 
    <input type="text" ngModel name="firstname" required #firstname="ngModel" />
</div>

Now that we have this reference, we can use it to check for the different kind of validation errors. There are different approaches to visualizing validation errors. Check out the Forms Validation cookbook on the Angular IO site for more details. For now we will simply hard code them in our template.

<div>
    Firstname: 
    <input type="text" ngModel name="firstname" required #firstname="ngModel" />
    <div style="color:red" 
        *ngIf="firstname.errors && (firstname.dirty || firstname.touched || form.submitted)">
        
        <p *ngIf="firstname.errors.required">
            The name is required
        </p>
    </div>
</div>

So what we did here is to add another <div> which contains the validation messages. The *ngIf you see, is there to only visualize our validation errors when our firstname field (pointing to the ngModel instance) actually contains errors (firstname.errors). Moreover, we also only want to only show it when either our fields is dirty, touched or when the form has been submitted.
 Within that div, we can then do some more fine-grained checks for the kind of error by accessing the firstname.errors property, in our case the firstname.errors.required.

Building a Custom Validator

Ok, now that you’ve seen how to add the build-in validators, let’s build the super useful “JuriNameValidator”, a validator that only allows “Juri” to be entered in a textbox. Sounds reasonable, doesn’t it?

So first we need to define and implement our so-called validation factory. Don’t let be scared away, it’s a simple function that builds and returns our validation function. We get an AbstractControl as parameter to our validation function that can be used to access the underlying field value, by using the according value property. If our validation check is valid, we return null, otherwise we return an object with a property juriName containing itself a property valid set to false.

import { AbstractControl, ValidatorFn } from '@angular/forms';

// validation function
function validateJuriNameFactory() : ValidatorFn {
  return (c: AbstractControl) => {
    
    let isValid = c.value === 'Juri';

    if(isValid) {
        return null;
    } else {
        return {
            juriName: {
                valid: false
            }
        };
  }
}

Once we have our validation factory funtion, we need to create a directive which mainly serves to attach our validation function onto an existing HTML input control. An Angular 2 directive is mostly like a component with the main difference that it doesn’t have a template.

@Directive({
  selector: '[juriName][ngModel]'
})
export class JuriNameValidator implements Validator { ... }

We register it on HTML controls having the attribute juriName as well as ngModel. Next, we connect our validation factory function with our directive. Note how in the constructor we call our factory function (which resides in this very same file) and associate it to our local validator variable. Note that here we could pass parameters to our valdiateJuriNameFactory which get passed as @Input to our directive.

function validateJuriNameFactory() : ValidatorFn { ... }

@Directive({...})
export class JuriNameValidator implements Validator {
  validator: ValidatorFn;
  
  constructor() {
    this.validator = validateJuriNameFactory();
  }
  
  validate(c: FormControl) {
    return this.validator(c);
  }
  
}

Great. But who calls the validate(...) function of our directive?? That’s a last missing step we have to do:

import { NG_VALIDATORS, Validator } from '@angular/forms';
...
@Directive({
  selector: '[juriName][ngModel]',
  providers: [
    { provide: NG_VALIDATORS, useExisting: JuriNameValidator, multi: true }
  ]
})
export class JuriNameValidator implements Validator { ... }

Finally we’re now ready to add our validator to our form which is as simple as adding juriName to our input control as well as defining a proper error message.

<div>
    Firstname: 
    <input type="text" 
        ngModel 
        name="firstname" 
        #firstname="ngModel" 
        required 
        juriName />
        
    <div style="color:red" *ngIf="firstname.errors && (firstname.dirty || firstname.touched || form.submitted)">
        <p *ngIf="firstname.errors.required">
            The name is required
        </p>
        <p *ngIf="firstname.errors.juriName">
            Only allowed name is "Juri" ;)
        </p>
    </div>
</div>

Final Code

Here’s the final code in a easy to use Plunk:

<!DOCTYPE html>
<html>

  <head>
    <base href="." />
    <title>angular2 playground</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <my-app>
    loading...
  </my-app>
  </body>

</html>


Many thanks to Sam Vloeberghs for reviewing this article.

AngularJS Form (document) Template

Published at DZone with permission of Juri Strumpflohner. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Top 5 Java REST API Frameworks
  • An Introduction to Data Mesh
  • Using JSON Web Encryption (JWE)
  • The Role of Data Governance in Data Strategy: Part II

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: