Introduction

The Angular module for AFD Postcode Evolution® (PCE) allows developers to easily harness the power of PCE in front-end Angular applications.

When filling out address forms, users simply need to start typing their address in a single TypeAhead box that will drill down on the results until the user can select their address from the list.

The plugin also has powerful validation tools that allow validation of phone numbers, email addresses, credit/debit cards, and UK bank accounts. In addition to validation it is possible to extract further information from the fields (for example "card type"), restrict input to certain keys and format the fields automatically, independent of the actual keys pressed by the user.

Compatibility

This module is compatible with Angular 6.0.0 onwards, it is not compatible with AngularJS.

Install and Setup the module

First install the module:

npm install --save @afd-software/pce-angular

Import it into your Angular project. We also need to import some key Angular components HttpClientModule and FormsModule

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { AfdPceAngularModule } from '@afd-software/pce-angular';
....
@NgModule({
  declarations: [
    AppComponent,
    ....
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    FormsModule,
    AfdPceAngularModule,
    ....
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Then in a component, preferably the top level component, we need to declare the AfdPceService and set our serial and password.

//app.component.ts
import {Component, OnInit} from '@angular/core';
import {AfdPceService} from '@afd-software/pce-angular';
@Component({
.....
})
export class AppComponent implements OnInit {
  constructor(
      public afdPceService: AfdPceService
  ){}
  ngOnInit(){
    this.afdPceService.settings.afdSerial = 'YOUR SERIAL';
    this.afdPceService.settings.afdPassword = 'YOUR PASSWORD';
  }
}

That's it, now you are ready to start adding controls to forms.

Address Lookup

"TypeAhead" functionality allows users to search for addresses and see results in real-time as they type. Our Angular TypeAhead controls make adding type ahead to an Angular project simple.

Typically with AFD PCE each time that a search term is submitted to our FastFind service, the customer is charged. However, with FastFind version four it is possible to group multiple related requests together using a common uniqueid and as long as the total number of results returned across the queries is 100 or less, the customer will only be charged once. As such, limiting the maximum number of results returned for a search to, for example, five, means that twenty key presses in a TypeAhead control will still only result in being charged once.

TypeAhead Control

To set up TypeAhead we need to add the component to our HTML template.

// app.component.html
<div class="form-control">
  <label for="afd-typeahead">TypeAhead</label>
  <afd-typeahead-component id="afd-typeahead"></afd-typeahead-component>
</div>

And add the AfdPceService to the component

// app.component.ts
import {Component} from '@angular/core';
import {AfdPceService} from '@afd-software/pce-angular';
....
export class AppComponent {
  constructor(
    public afdPceService: AfdPceService
  ) {
    this.afdPceService.selectedCountry = null; // Ensure that the country is UK
  }
}

That's it, now when you start typing an address in the box you will be presented with results (5 by default).

International Searches

There are two ways to specify a country for international searches, static country selection and dynamic country selection.

Static Country Selection

This method is for situations in which there will only ever be one country set for searches. To specify the country add the country= attribute to the <afd-typeahead-component> and specify the ISO3 code for that country.

// app.component.html
<div class="form-control">
  <label for="afd-typeahead">TypeAhead</label>
  <afd-typeahead-component country="USA" id="afd-typeahead"></afd-typeahead-component>
</div>

Dynamic Country Selection

This method allows users to update the country. Essentially an object that contains at least the iso code should be set as the selectedCountry on AfdPceService.

// app.component.ts
import {Component} from '@angular/core';
import {AfdPceService} from '@afd-software/pce-angular';
....
export class AppComponent implements OnInit {
  constructor(
    public afdPceService: AfdPceService
  ) {
  }
  ngOnInit(){
    this.afdPceService.selectedCountry = {
      name: "United States of America",
	  iso: "USA"
    }
  }
}

Developers are free to supply this object to the service in anyway that they like, however, we also supply a directive that will populate a <select> component with the countries supported by WorldAddress

The following example shows how to add a country select control to a form:

<select afdCountries="FRA" [(ngModel)]="afdPceService.selectedCountry">
  <option *ngFor="let country of afdPceService.countries" [ngValue]="country">
    {{country.name}}
  </option>
</select>

They key points here are:

  • afdCountries This directive triggers a query against PCE to get a list of the available countries. On return this populates the countries array in AfdPceService.
  • ="FRA" Optionally a country can be set as a default country in the select list, in this case France. Only ISO3 country codes are accepted.
  • afdPceService.countries As previously mentioned, this is the array that contains a collection of country options.

Results Controls

Once the user selects a result from the TypeAhead control, the module will query PCE to retrieve the full result for the selected address and send it to the results fields.

For setting results, we have a directive, afdResult, that should be placed on an input control. This control will then receive the specified PCE field.

<div class="form-control">
  <label for="afd-typeahead">TypeAhead</label>
  <afd-typeahead-component id="afd-typeahead"></afd-typeahead-component>
</div>
<div class="form-control">
  <label for="postcode">Postcode</label>
  <input afdResult="Postcode" id="postcode">
</div>
<div class="form-control">
  <label for="organisation">Organisation</label>
  <input afdResult="Organisation" id="organisation">
</div>
<div class="form-control">
  <label for="property">Property</label>
  <input afdResult="Property" id="property">
</div>
<div class="form-control">
  <label for="street">Street</label>
  <input afdResult="Street" id="street">
</div>
<div class="form-control">
  <label for="town">Town</label>
  <input afdResult="Town" id="town">
</div>
<div class="form-control">
  <label for="county">County</label>
  <input afdResult="TraditionalCounty" id="county">
</div>

All PCE fields are accessible and are assigned using the afdResult directive.

Events

There is a single event, afdRetrieveComplete,that is fired on the document when a retrieve has been completed. This is a good way to look at the available fields or add some extra logic once the retrieve task has been completed.

To access this, the following code can be added the following code to the component:

// app.component.ts
import {Component, HostListener} from '@angular/core';
....
export class AppComponent {
  @HostListener('document:afdRetrieveComplete', ['$event'])  
  onAfdRetrieveComplete(e) {
    console.log(e.detail['Item'][0]);
  }
  constructor(
    public afdPceService: AfdPceService,
  ) {}
}

Styling

By default the control has no style applied to it so that developers can easily apply their own styles. We do supply a css file with some sample code that can be used as a starter:

/node_modules/@afd-software/pce-angualr/typeahead.css

Angular Material

If you are using the Angular Material design framework we have a prebuilt Autocomplete control.

First install the @afd-software/pce-angular-material module. This is in addition to the base @afd-software/angular-pce modue.

npm install --save @afd-software/pce-angular-material

Then import the module into you project:

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { AfdPceAngularModule } from '@afd-software/pce-angular';
import { AfdPceAngularMaterialModule } from '@afd-software/pce-angular-material';
....
@NgModule({
  declarations: [
    AppComponent,
    ....
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    FormsModule,
    AfdPceAngularModule,
    AfdPceAngularMaterialModule,
    ....
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Then in your template:

// app.component.html
<afd-material-type-ahead-component></afd-material-type-ahead-component>
  <mat-form-field >
    <input matInput afdResult="Postcode" placeholder="Postcode">
  </mat-form-field>
  <mat-form-field >
    <input matInput afdResult="DPS" placeholder="DPS" >
  </mat-form-field>
  <mat-form-field >
    <input matInput afdResult="Property" placeholder="Property" >
  </mat-form-field>
  <mat-form-field >
    <input matInput afdResult="Organisation" placeholder="Organisation" >
  </mat-form-field>
  <mat-form-field >
    <input matInput afdResult="Street" placeholder="Street" >
  </mat-form-field>
  <mat-form-field >
    <input matInput afdResult="Locality" placeholder="Locality" >
  </mat-form-field>
  <mat-form-field >
    <input matInput afdResult="Town" placeholder="Town" >
  </mat-form-field>

Credit Card Validation

Card validation happens via three attribute directives. The first two of these check that the structure of the card number and the expiry date are correct. If both of these checks return valid then a validation directive that is placed on the parent form will fire and validate the pair of values against PCE. In addition to validation it is also possible to mask the inputs so that they are formatted correctly and extract information about the card.

Validation

As each call to PCE is charged for we do not want to send validation requests for numbers that are clearly not valid. As such, we first try to validate the card number and expiry date via a regular expression check and only when the patterns are valid do we request validation from PCE. For this first stage we have two attribute directives afdCardRegexValid and afdExpiryRegexValid, these should each be placed on the relevant input.

Once both of these controls are valid we then want to query PCE to validate the combination. To do this we place the afdCardPceValid directive to the parent form.

Important: It is essential that you include both of these base directives in the controls. If you only use afdCardPceValid you will request validation from PCE on each keystroke which will raise excessive charges.

<form #cardForm="ngForm" afdCardPceValid>
  <div class="form-field">
    <input
      name="cardControl"
      type="text"
      afdCardRegexValid
    >
  <div class="form-control">
    <input
      name="expiryControl"
      type="text"
      afdExpiryRegexValid
    >
  </div>
</form>

The error state of the controls are accessible via both the Angular Form Control API and the Constraint Validation API.

We recommend using the form control API, to do so you must specify a template reference variable and set its value to ngModel on the inputs and ngForm on the form (e.g. #cardControl="ngModel"). You also need to bind ngModel to a variable in your class that will hold the value of the control.

When there is a regex validation error on either of the controls or on the form, one of the objects afdCardRegexValid, afdExpiryPceValid or afdCardPceValid depending on the origin will be added to the errors object of the control or form. The object contains an object with a single key value pair {message: 'Error Message'}. This error can be customised in the settings object of the afdPceService.

The named control (e.g. cardControl) is accessible both in the template, and also can be sent to the class via events such as (ngModelChange).

In the template it is useful to add helper messages that display when there is a validation error. *ngIf can be used to display errors depending on the error that is displayed using the .hasError function.

<!--app.component.html-->
<form #cardForm="ngForm" afdCardPceValid>
  <div class="form-field">
    <input
      name="cardControl"
      type="text"
      afdCardRegexValid
      [(ngModel)]="card"
      #cardControl="ngModel"
      (ngModelChange)="changeModel($event, cardForm, cardControl, expiryControl)"
    >
    <span class="error" *ngIf="cardControl.hasError('afdCardRegexValid') && cardControl.touched">
      {{cardControl.errors['afdCardRegexValid'].message}}
    </span>
  </div>
  <div class="form-control">
    <input
      name="expiryControl"
      type="text"
      afdExpiryRegexValid
      [(ngModel)]="expiry"
      #expiryControl="ngModel"
      (ngModelChange)="changeModel($event, cardForm, cardControl, expiryControl)"
    >
    <span class="error" *ngIf="expiryControl.hasError('afdExpiryRegexValid') && expiryControl.touched">
      {{expiryControl.errors['afdExpiryRegexValid'].message}}
    </div>
  </div>
  <span class="error" *ngIf="cardControl.hasError('afdCardPceValid') && cardControl.touched">
    {{cardControl.errors['afdCardPceValid'].message}}
  </span>
</form>
// app.component.ts
// ....
export class AppComponent{
    // ....
    changeModel(val, form, cardControl, expiryControl){
      console.log(val);
      console.log(form);
      console.log(cardControl);
      console.log(expiryControl);
    }
    // ....
}

Additional Information

It is possible to know some things about a card even before its number has been input in its entirety. We allow access to this via an Observable on the AfdPceService.

To access this in your component use the following code...

// app.component.ts
import {Component, OnInit} from '@angular/core';
import {AfdPceService} from '@afd-software/pce-angular';
import {Subscription} from 'rxjs';
....
export class AppComponent implements OnInit {
    ....
    cardInfoSubscription: Subscription;
    cardInfo = null;
    constructor(
        private afdPceService: AfdPceService
    ) {}
    ngOnInit() {
        ....
        this.cardInfoSubscription = this.afdPceService.cardInfo.subscribe(data => {
            this.CardInfo = data;
        });
    }
}

... and then in your template...

<!--app.component.html-->
<form #cardForm="ngForm" afdCardPceValid>
  <div class="form-field">
    <input
      name="cardControl"
      type="text"
      afdCardRegexValid
    >
  </div>
  <div class="form-control">
    <input
      name="expiryControl"
      type="text"
      afdExpiryRegexValid
    >
  </div>
</form>
<ul *ngIf="cardInfo">
  <li>
    <strong>Card Regex Valid:</strong>{{cardInfo.cardRegexValid}}
  </li>
  <li>
    <strong>Expiry Regex Valid:</strong>{{cardInfo.expiryRegexValid}}
  </li>
  <li>
    <strong>PCE Valid:</strong>{{cardInfo.pceValid}}
  </li>
  <li>
    <strong>Card Type Nice:</strong>{{cardInfo.cardTypeNice}}
  </li>
  <li>
    <strong>Card Type:</strong>{{cardInfo.cardType}}
  </li>
  <li>
    <strong>Card Number: </strong>{{cardInfo.cardNumber}}
  </li>
  <li>
    <strong>Expiry Date: </strong>{{cardInfo.expiryDate}}
  </li>
</ul>

The full cardInfo object looks like this:

cardInfo: {
  pceValid: boolean,
  cardRegexValid: boolean,
  expiryRegexValid: boolean,
  cardTypeNice: string,
  cardType: string,
  cardNumber: string,
  expiryDate: string
};

Masking

It is possible to apply masks to the inputs to prevent invalid characters from being added and to format the inputs correctly. The card will add the spaces correctly to the field depending on the card type and the expiry date will be formatted in a mm / yyyy format. To do this simply add the [textmask] directive to the inputs as shown below.

<form #cardForm="ngForm" afdCardPceValid>
  <div class="form-field">
    <input
      name="cardControl"
      type="text"
      afdCardRegexValid
      [textMask]="afdPceService.cardMask"
    >
  </div>
  <div class="form-field">
    <input
      name="expiryControl"
      type="text"
      afdExpiryRegexValid
      [textMask]="afdPceService.expiryMask"
    >
  </div>
</form>

Angular Material Example

Below is an example template for using Angular Material with the card validation directive:

// app.component.html
<mat-card fxFlex="" fxLayout="column">
  <mat-card-title>
    <h2 class="mat-headline">Card Number Validation</h2>
  </mat-card-title>
  <mat-card-content fxFlex fxLayout="column">
    <form #cardForm="ngForm" afdCardPceValid>
      <mat-form-field>
        <input
          name="cardControl"
          type="text"
          matInput
          afdCardRegexValid
          placeholder="Card Number"
          [(ngModel)]="card"
          #cardControl="ngModel"
          [textMask]="afdPceService.cardMask"
        >
        <mat-error *ngIf="cardControl.hasError('required')">{{afdPceService.settings.messages.phone.required}}
        </mat-error>
        <mat-error *ngIf="cardControl.hasError('afdCardRegexValid')">{{cardControl.errors['afdCardRegexValid'].message}}
        </mat-error>
      </mat-form-field>
      <mat-form-field>
        <input
          name="expiryControl"
          type="text"
          matInput
          afdExpiryRegexValid
          placeholder="Expiry Date"
          [(ngModel)]="expiry"
          #expiryControl="ngModel"
          [textMask]="afdPceService.expiryMask"
        >
        <mat-error *ngIf="expiryControl.hasError('required')">{{afdPceService.settings.messages.card.expiryRequired}}
        </mat-error>
        <mat-error *ngIf="expiryControl.hasError('afdExpiryRegexValid')">
          {{expiryControl.errors['afdExpiryRegexValid'].message}}
        </mat-error>
      </mat-form-field>
      <mat-error *ngIf="cardForm.hasError('afdCardPceValid')">{{cardForm.errors['afdCardPceValid'].message}}
      </mat-error>
    </form>
  </mat-card-content>
  <mat-list *ngIf="cardInfo">
    <mat-list-item>
      <strong>Card Regex Valid:</strong>{{cardInfo.cardRegexValid}}
    </mat-list-item>
    <mat-list-item>
      <strong>Expiry Regex Valid:</strong>{{cardInfo.expiryRegexValid}}
    </mat-list-item>
    <mat-list-item>
      <strong>PCE Valid:</strong>{{cardInfo.pceValid}}
    </mat-list-item>
    <mat-list-item>
      <strong>Card Type Nice:</strong>{{cardInfo.cardTypeNice}}
    </mat-list-item>
    <mat-list-item>
      <strong>Card Type:</strong>{{cardInfo.cardType}}
    </mat-list-item>
    <mat-list-item>
      <strong>Card Number: </strong>{{cardInfo.cardNumber}}
    </mat-list-item>
    <mat-list-item>
      <strong>Expiry Date: </strong>{{cardInfo.expiryDate}}
    </mat-list-item>
  </mat-list>
</mat-card>
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { AfdPceService } from '@afd-software/pce-angular';
import { Subscription } from 'rxjs/Subscription';
@Component({
  selector: 'app-component',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent implements OnInit {
  cardInfoSubscription: Subscription;
  cardInfo = null;
  card;
  expiry;
  constructor(
    public afdPceService: AfdPceService
  ) {}
  ngOnInit() {
    this.cardInfoSubscription = this.afdPceService.cardInfo.subscribe(data => {
      this.cardInfo = data;
    });
  }
}

Bank Account Validation

Bank Account validation happens via three attribute directives. The first two of these check that the structure of the account number and the sort code are correct. If both of these checks return valid then a validation directive that is placed on the parent form will fire and validate the pair of values against PCE. In addition to validation it is also possible to mask the inputs so that they are formatted correctly, and also extract information about the bank account.

Validation

As each call to PCE is charged for we do not want to send validation requests for numbers that are clearly not valid. As such, we first try to validate the account number and sort code via a regular expression check and only when the patterns are valid do we request validation from PCE. For this first stage we have two attribute directives afdAccountRegexValid and afdSortRegexValid, these should each be placed on the relevant input.

Once both of these controls are valid we then want to query PCE to validate the combination. To do this we place the afdAccountPceValid directive to the parent form.

Important: It is essential that you include both of these base directives in the controls. If you only use afdAccountPceValid you will request validation from PCE on each keystroke which will raise excessive charges.

<form #accountForm="ngForm" afdAccountPceValid>
  <div class="form-field">
    <input
      name="accountControl"
      type="text"
      afdAccountRegexValid
    >
  <div class="form-control">
    <input
      name="sortControl"
      type="text"
      afdSortRegexValid
    >
  </div>
</form>

The error state of the controls are accessible via both the Angular Form Control API and the Constraint Validation API.

We recommend using the form control API, to do so you must specify a template reference variable and set its value to ngModel on the inputs and ngForm on the form (e.g. #accountControl="ngModel"). You also need to bind ngModel to a variable in your class that will hold the value of the control.

When there is a regex validation error on either of the controls or on the form, one of the objects afdAccountRegexValid, afdSortPceValid or afdAccountPceValid depending on the origin will be added to the errors object of the control or form. The object contains an object with a single key value pair {message: 'Error Message'}. This will be an error either set in the settings object of the afdPceService.

The named control (e.g. accountControl) is accessible both in the template, and also can be sent to the class via events such as (ngModelChange).

In the template it is useful to add helper messages that display when there is a validation error. *ngIf can be used to display errors depending on the error that is displayed using the .hasError function.

<!--app.component.html-->
<form #accountForm="ngForm" afdAccountPceValid>
  <div class="form-field">
    <input
      name="accountControl"
      type="text"
      afdAccountRegexValid
      [(ngModel)]="account"
      #accountControl="ngModel"
      (ngModelChange)="changeModel($event, accountForm, accountControl, sortControl)"
    >
    <span class="error" *ngIf="accountControl.hasError('afdAccountRegexValid') && accountControl.touched">
      {{accountControl.errors['afdAccountRegexValid'].message}}
    </span>
  </div>
  <div class="form-control">
    <input
      name="sortControl"
      type="text"
      afdSortRegexValid
      [(ngModel)]="sort"
      #sortControl="ngModel"
      (ngModelChange)="changeModel($event, accountForm, accountControl, sortControl)"
    >
    <span class="error" *ngIf="sortControl.hasError('afdSortRegexValid') && sortControl.touched">
      {{sortControl.errors['afdSortRegexValid'].message}}
    </div>
  </div>
  <span class="error" *ngIf="accountControl.hasError('afdAccountPceValid') && accountControl.touched">
    {{accountControl.errors['afdAccountPceValid'].message}}
  </span>
</form>
// app.component.ts
// ....
export class AppComponent{
    // ....
    changeModel(val, form, accountControl, sortControl){
      console.log(val);
      console.log(form);
      console.log(accountControl);
      console.log(sortControl);
    }
    // ....
}

Additional Information

It is possible to know some things about an account even before its number has been input in its entirety. We allow access to this via an Observable on the AfdPceService.

To access this in your component use the following code...

// app.component.ts
import {Component, OnInit} from '@angular/core';
import {AfdPceService} from '@afd-software/pce-angular';
import {Subscription} from 'rxjs';
....
export class AppComponent implements OnInit {
    ....
    accountInfoSubscription: Subscription;
    accountInfo = null;
    constructor(
        private afdPceService: AfdPceService
    ) {}
    ngOnInit() {
        ....
        this.accountInfoSubscription = this.afdPceService.accountInfo.subscribe(data => {
            this.accountInfo = data;
        });
    }

... and then in your template...

<!--app.component.html-->
<form #accountForm="ngForm" afdAccountPceValid>
  <div class="form-field">
    <input
      name="accountControl"
      type="text"
      afdAccountRegexValid
    >
  </div>
  <div class="form-control">
    <input
      name="sortControl"
      type="text"
      afdAccountRegexValid
    >
  </div>
</form>
<ul>
    <li>
      <strong>Account Number Regex Valid: </strong>&nbsp;{{ accountInfo.accountRegexValid }}
    </li>
    <li>
      <strong>Sort Code Regex Valid:</strong>&nbsp;{{accountInfo.sortRegexValid}}
    </li>
    <li>
      <strong>PCE Valid: </strong>&nbsp;{{accountInfo.pceValid}}
    </li>
    <li>
      <strong>IBAN:</strong>&nbsp;{{accountInfo.iban}}
    </li>
    <li>
      <strong>Clearing System:</strong>&nbsp;{{accountInfo.clearingSystem}}
    </li>
    <li>
      <strong>Account Type:</strong>&nbsp;{{accountInfo.accountType}}
    </li>
    <li>
      <strong>Account Number:</strong>&nbsp;{{accountInfo.accountNumber}}
    </li>
    <li>
      <strong>Sort Code:</strong>&nbsp;{{accountInfo.sortCode}}
    </li>
  </ul>

The full accountInfo object looks like this:

accountInfo: {
  pceValid: boolean,
  accountRegexValid: boolean,
  sortRegexValid: boolean,
  iban: string,
  clearingSystem: string,
  rollNumber: string,
  accountType: string,
  accountNumber: string,
  sortCode: string
}

Masking

It is possible to apply masks to the inputs to prevent invalid characters from being added and to format the inputs correctly. The account field will only allow for eight numerical characters and the sortcode will be formatted in a 00-00-00 format. To do this simply add the [textmask] directive to the inputs as shown below.

<form #accountForm="ngForm" afdAccountPceValid>
  <div class="form-field">
    <input
      name="accountControl"
      type="text"
      afdAccountRegexValid
      [textMask]="afdPceService.accountMask"
    >
  </div>
  <div class="form-field">
    <input
      name="sortControl"
      type="text"
      afdSortRegexValid
      [textMask]="afdPceService.sortMask"
    >
  </div>
</form>

Angular Material Example

Below is an example template for using Angular Material with the bank account validation directive:

// app.component.html
<mat-card fxFlex="" fxLayout="column">
  <mat-card-title>
    <h2 class="mat-headline">Account Number Validation</h2>
  </mat-card-title>
  <mat-card-content fxFlex fxLayout="column">
    <form #accountForm="ngForm" afdAccountPceValid>
      <mat-form-field>
        <input
          name="accountControl"
          type="text"
          matInput
          afdAccountRegexValid
          placeholder="Account Number"
          [(ngModel)]="account"
          #accountControl="ngModel"
          [textMask]="afdPceService.accountMask"
        >
        <mat-error *ngIf="accountControl.hasError('required')">{{afdPceService.settings.messages.account.required}}</mat-error>
        <mat-error *ngIf="accountControl.hasError('afdAccountRegexValid')">{{accountControl.errors['afdAccountRegexValid'].message}}
        </mat-error>
      </mat-form-field>
      <mat-form-field>
        <input
          name="sortControl"
          type="text"
          matInput
          afdSortRegexValid
          placeholder="Sort Code"
          [(ngModel)]="sort"
          #sortControl="ngModel"
          [textMask]="afdPceService.sortMask"
        >
        <mat-error *ngIf="sortControl.hasError('required')">{{afdPceService.settings.messages.account.sortRequired}}</mat-error>
        <mat-error *ngIf="sortControl.hasError('afdSortRegexValid')">{{sortControl.errors['afdSortRegexValid'].message}}
        </mat-error>
      </mat-form-field>
      <mat-error *ngIf="accountForm.hasError('afdAccountPceValid') && accountForm.touched">{{accountForm.errors['afdAccountPceValid'].message}}
      </mat-error>
    </form>
  </mat-card-content>
  <mat-list>
    <mat-list-item>
      <strong>Account Number Regex Valid: </strong>&nbsp;{{ accountInfo.accountRegexValid }}
    </mat-list-item>
    <mat-list-item>
      <strong>Sort Code Regex Valid:</strong>&nbsp;{{accountInfo.sortRegexValid}}
    </mat-list-item>
    <mat-list-item>
      <strong>PCE Valid: </strong>&nbsp;{{accountInfo.pceValid}}
    </mat-list-item>
    <mat-list-item>
      <strong>IBAN:</strong>&nbsp;{{accountInfo.iban}}
    </mat-list-item>
    <mat-list-item>
      <strong>Clearing System:</strong>&nbsp;{{accountInfo.clearingSystem}}
    </mat-list-item>
    <mat-list-item>
      <strong>Account Type:</strong>&nbsp;{{accountInfo.accountType}}
    </mat-list-item>
    <mat-list-item>
      <strong>Account Number:</strong>&nbsp;{{accountInfo.accountNumber}}
    </mat-list-item>
    <mat-list-item>
      <strong>Sort Code:</strong>&nbsp;{{accountInfo.sortCode}}
    </mat-list-item>
  </mat-list>
</mat-card>
// app.component.ts
import {Component, HostListener, Output, EventEmitter, OnInit} from '@angular/core';
import { AfdPceService, Countries } from '@afd-software/pce-angular';
import {Subscription} from 'rxjs/Subscription';
@Component({
  selector: 'app-component[fxLayout="column"]',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent implements OnInit {
  accountInfoSubscription: Subscription;
  accountInfo = null;
  account;
  sort;
  constructor(
    public afdPceService: AfdPceService
  ) {}
  ngOnInit() {
    this.accountInfoSubscription = this.afdPceService.accountInfo.subscribe(data => {
      this.accountInfo = data;
    });
  }
}

Phone Number Validation

Phone validation happens via two attribute directives that are added to to an input control. In addition to validation it is also possible to mask the input so that it is formatted like a phone number, and extract information about the phone number.

Validation

As each call to PCE is charged for we do not want to send validation requests for numbers that are clearly not valid. As such, we first try to validate the phone number via Regex and only when the pattern is valid do we request validation from PCE. For this we have two attribute directives afdPhoneRegexValid and afdPhonePceValid.

Important: It is essential that you include both of these directives. If you only use afdPhonePceValid you will request validation from PCE on each keystroke which will raise excessive charges.

You can optionally provide a [country] (ISO2). This is the country that the control will assume if no country code is input. Specifying a country when typing, either using + or '00' at the beginning of the number will override this option.

<input
  type="tel"
  [country]="'US'"
  afdPhoneRegexValid
  afdPhonePceValid
>

The error state of the control is accessible via both the Angular Form Control API and the Constraint Validation API.

We recommend using the form control API, to do so you must specify a template reference variable and set its value to ngModel (e.g. #phoneControl="ngModel"). You also need to bind ngModel to a variable in your class that will hold the value of the control.

When there is a validation error one of the objects afdPhoneRegexValid or afdPhonePceValid depending on the origin will be added to the errors object of the control. The object contains an object with a single key value pair {message: 'Error Message'}. This will be an error either set in the settings or returned from PCE.

The phoneControl is accessible both in the template, and also can be sent to the class via events (such as (ngModelChange).

In the template it is useful to add helper messages that display when there is a validation error. *ngIf can be used to display errors depending on the error that is displayed using the .hasError function.

<!--app.component.html-->
<div class="form-control">
  <input
    type="tel"
    afdPhoneRegexValid
    afdPhonePceValid
    #phoneControl="ngModel"
    [(ngModel)]="phone"
    (ngModelChange)="changeModel($event, phoneControl)" <!-- you can listen to changes using   ngModelChange -->
  >
  <span *ngIf="phoneControl.hasError('required')" class="error">This field is required</span>
  <span *ngIf="phoneControl.hasError('afdPhoneRegexValid')" class="error">{{phoneControl.errors.afdPhoneRegexValid.message}}</span>
  <span *ngIf="phoneControl.hasError('afdPhonePceValid')" class="error">{{phoneControl.errors.afdPhonePceValid.message}}</span>
</div>
// app.component.ts
// ....
export class AppComponent{
    // ....
    changeModel(val, control){
        console.log(val);
        console.log(control);
    }
    // ....
}

The validator will also format the phone number according to the formatting rules of the country of the phone.

Additional Information

It is possible to know some things about a phone number even before it has been input in its entirety. We allow access to this via an Observable on the AfdPceService.

To access this in your component use the following code:

// app.component.ts
import {Component, OnInit} from '@angular/core';
import {AfdPceService} from '@afd-software/pce-angular';
import {Subscription} from 'rxjs';
....
export class AppComponent implements OnInit {
    ....
    phoneInfoSubscription: Subscription;
    phoneInfo = null;
    constructor(
        private afdPceService: AfdPceService
    ) {}
    ngOnInit() {
        ....
        this.phoneInfoSubscription = this.afdPceService.phoneInfo.subscribe(data => {
            this.phoneInfo = data;
        });
    }

... and then in your template...

<!--app.component.html-->
<div class="form-control">
  <input
    type="tel"
    afdPhoneRegexValid
    afdPhonePceValid
  >
</div>
<div class="phone-info">
  <ul>
    <li>{{ phoneInfo.regexValid }}</li>
    <li>{{ phoneInfo.country.name }}</li>
  </ul>
<div>

The full phoneInfo object looks like this:

phoneInfo: {
  pceValid: boolean,
  regexValid: boolean,
  country: {
    name: string,
    iso2: string,
    iso3: string
  },
  isMobile: boolean,
  isLandLine: boolean
};

Masking

It is possible to apply a mask to the input that prevents the input of anything except numbers and a "+" symbol in the first position of the input. To do this simply add the [textmask] directive to the input as shown below.

<input
    type="tel"
    afdPhoneRegexValid
    afdPhonePceValid
    #phoneControl="ngModel"
    [textMask]="afdPceService.phoneMask"
>

Angular Material Example

Below is an example template for using Angular Material with the phone validation directive:

// app.component.html
<mat-card fxFlex="" fxLayout="column">
  <mat-card-title>
    <h2 class="mat-headline">Phone Number Validation</h2>
  </mat-card-title>
  <mat-card-content fxFlex fxLayout="column">
    <mat-form-field>
      <input
        type="tel"
        [country]="'US'"
        matInput
        afdPhoneRegexValid
        afdPhonePceValid
        [textMask]="afdPceService.phoneMask"
        [(ngModel)]="phone"
        #phoneControl="ngModel"
      >
      <mat-error *ngIf="phoneControl.hasError('required')">{{afdPceService.settings.messages.phone.required}}</mat-error>
      <mat-error *ngIf="phoneControl.hasError('afdPhoneRegexValid')">{{phoneControl.errors['afdPhoneRegexValid'].message}}
      </mat-error>
      <mat-error *ngIf="phoneControl.hasError('afdPhonePceValid')">{{phoneControl.errors['afdPhonePceValid'].message}}
      </mat-error>
    </mat-form-field>
    <mat-list *ngIf="phoneInfo">
      <mat-list-item>
        <strong>Regex Valid: </strong><span>&nbsp;{{ phoneInfo.regexValid }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>PCE Valid: </strong><span>&nbsp;{{ phoneInfo.pceValid }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>Country: </strong><span>&nbsp;{{ phoneInfo.country.name }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>Country ISO2: </strong><span>&nbsp;{{ phoneInfo.country.iso2 }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>Country ISO3: </strong><span>&nbsp;{{ phoneInfo.country.iso3 }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>Is Mobile: </strong><span>&nbsp;{{ phoneInfo.isMobile }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>Is LandLine: </strong><span>&nbsp;{{ phoneInfo.isLandLine }}</span>
      </mat-list-item>
    </mat-list>
  </mat-card-content>
</mat-card>
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { AfdPceService, Countries } from '@afd-software/pce-angular';
import { MatDialog } from '@angular/material';
import { Subscription } from 'rxjs/Subscription';
@Component({
  selector: 'app-component',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent implements OnInit {
  phone;
  countries = Countries;
  phoneInfoSubscription: Subscription;
  hasPhoneInfo = false;
  phoneInfo = null;
  constructor(
    public afdPceService: AfdPceService
  ) {
    this.afdPceService.phoneCountry = this.countries[234];
  }
  ngOnInit() {
    this.phoneInfoSubscription = this.afdPceService.phoneInfo.subscribe(data => {
      this.phoneInfo = data;
    });
  }
}

Email Address Validation

Email validation happens via two attribute directives that are added to to an input control.

Validation

As each call to PCE is charged for we do not want to send validation requests for email addresses that are clearly not valid. As such, we first try to validate the email address via Regex and only when the pattern is valid do we request validation from PCE. For this we have two attribute directives afdEmailRegexValid and afdEmailPceValid.

Important: It is essential that you include both of these directives. If you only use afdEmailPceValid you will request validation from PCE on each keystroke which will raise excessive charges.

<input
  type="email"
  afdEmailRegexValid
  afdEmailPceValid
>

The error state of the control is accessible via both the Angular Form Control API and the Constraint Validation API.

We recommend using the form control API, to do so you must specify a template reference variable and set its value to ngModel (e.g. #emailControl="ngModel"). You also need to bind ngModel to a variable in your class that will hold the value of the control.

When there is a validation error one of the objects afdEmailRegexValid or afdEmailPceValid depending on the origin will be added to the errors object of the control. The object contains an object with a single key value pair {message: 'Error Message'}. This will be an error either set in the settings or returned from PCE.

The emailControl is accessible both in the template, and also can be sent to the class via events (such as (ngModelChange).

In the template it is useful to add helper messages that display when there is a validation error. *ngIf can be used to display errors depending on the error that is displayed using the .hasError function.

<!--app.component.html-->
<div class="form-control">
  <input
    type="email"
    afdEmailRegexValid
    afdEmailPceValid
    #emailControl="ngModel"
    [(ngModel)]="email"
    (ngModelChange)="changeModel($event, emailControl)" <!-- you can listen to changes using   ngModelChange -->
  >
  <span *ngIf="emailControl.hasError('required')" class="error">This field is required</span>
  <span *ngIf="emailControl.hasError('afdEmailRegexValid')" class="error">{{emailControl.errors.afdEmailRegexValid.message}}</span>
  <span *ngIf="emailControl.hasError('afdEmailPceValid')" class="error">{{emailControl.errors.afdEmailPceValid.message}}</span>
</div>
// app.component.ts
// ....
export class AppComponent{
    // ....
    changeModel(val, control){
        console.log(val);
        console.log(control);
    }
    // ....
}

Additional Information

It is possible to know some extra things about an email address. We allow access to this via an Observable on the AfdPceService.

To access this in your component use the following code...

// app.component.ts
import {Component, OnInit} from '@angular/core';
import {AfdPceService} from '@afd-software/pce-angular';
import {Subscription} from 'rxjs';
....
export class AppComponent implements OnInit {
    ....
    emailInfoSubscription: Subscription;
    emailInfo = null;
    constructor(
        private afdPceService: AfdPceService
    ) {}
    ngOnInit() {
        ....
        this.emailInfoSubscription = this.afdPceService.emailInfo.subscribe(data => {
            this.emailInfo = data;
        });
    }

... and then in your template...

<!--app.component.html-->
<div class="form-control">
  <input
    type="tel"
    afdEmailRegexValid
    afdEmailPceValid
  >
</div>
<div *ngIf="emailInfo" class="email-info">
  <ul>
    <li>{{ emailInfo.regexValid }}</li>
    <li>{{ emailInfo.pceValidationMessage }}</li>
  </ul>
<div>

The full emailInfo object looks like this:

emailInfo: {
  pceValid: boolean,
  regexValid: boolean,
  pceValidationMessage: string,
  emailAddress: string
};

Angular Material Example

Below is an example template for using Angular Material with the email validation directive:

// app.component.html
<mat-card fxFlex="" fxLayout="column">
  <mat-card-title>
    <h2 class="mat-headline">Email Validation</h2>
  </mat-card-title>
  <mat-card-content fxFlex fxLayout="column">
    <mat-form-field>
      <input
        type="email"
        matInput
        afdEmailRegexValid
        afdEmailPceValid
        [(ngModel)]="email"
        #emailControl="ngModel"
      >
      <mat-error *ngIf="emailControl.hasError('required')">{{afdPceService.settings.messages.email.required}}</mat-error>
      <mat-error *ngIf="emailControl.hasError('afdPhoneRegexValid')">{{emailControl.errors['afdEmailRegexValid'].message}}
      </mat-error>
      <mat-error *ngIf="emailControl.hasError('afdEmailPceValid')">{{emailControl.errors['afdEmailPceValid'].message}}
      </mat-error>
    </mat-form-field>
    <mat-list *ngIf="emailInfo">
      <mat-list-item>
        <strong>Regex Valid: </strong><span>&nbsp;{{ emailInfo.regexValid }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>PCE Valid: </strong><span>&nbsp;{{ emailInfo.pceValid }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>PCE Message: </strong><span>&nbsp;{{ emailInfo.pceValidationMessage }}</span>
      </mat-list-item>
      <mat-list-item>
        <strong>Email Address: </strong><span>&nbsp;{{ emailInfo.emailAddress }}</span>
      </mat-list-item>
    </mat-list>
  </mat-card-content>
</mat-card>
// app.component.ts
import {Component, HostListener, OnInit} from '@angular/core';
import {AfdPceService } from '@afd-software/pce-angular';
import {Subscription} from 'rxjs/Subscription';
@Component({
  selector: 'app-component',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent implements OnInit{
  email;
  emailInfoSubscription: Subscription;
  emailInfo = null;
  constructor(
    public afdPceService: AfdPceService
  ) {}
  ngOnInit() {
    this.emailInfoSubscription = this.afdPceService.emailInfo.subscribe(data => {
      this.emailInfo = data;
    });
  }
}