Defining custom control validators

Another data property defined in the Developer class is the email field. Let’s add an input field for this property. Above the button in the preceding form, add the following markup:

<div class="form-group">
  <label class="control-label" for="emailInput">Email</label>
  <div>
    <input id="emailInput"
           class="form-control"
           type="text" ngControl="email"
     [(ngModel)]="developer.email"/>
  </div>
</div>

We can think of the [(ngModel)] attribute as an alternative to the ng-model directive from AngularJS 1.x. We will explain it in detail in the Two-way data binding with Angular 2 section.

 

Although Angular 2 provides a set of predefined validators, they are not enough for all the various formats our data can live in. Sometimes, we’ll need custom validation logic for our application-specific data. For instance, in this case, we want to define an e-mail validator. A typical regular expression, which works in general cases (but does not cover the entire specification that defines the format of the e-mail addresses), looks as follows: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.

In ch6/ts/step-1/add_developer.ts, define a function that accepts an instance of Angular 2 control as an argument and returns null if the control’s value is empty or matches the regular expression mentioned earlier, and { 'invalidEmail': true } otherwise:

function validateEmail(emailControl) {
  if (!emailControl.value ||
    /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test(emailControl.value)) {
    return null;
  } else {
    return { 'invalidEmail': true };
  }
}

Now, from the modules angular2/common and angular2/core import NG_VALIDATORS and Directive, and wrap this validation function within the following directive:

@Directive({
  selector: '[email-input]',
  providers: [provide(NG_VALIDATORS, {
    useValue: validateEmail, multi: true
  })]
})
class EmailValidator {}

In the preceding code, we defined a single multiprovider for the token NG_VALIDATORS. Once we inject the value associated with this token, we’ll get an array with all the validators attached to the given control

The only two steps left in order to make our custom validation work are to first add the email-input attribute to the e-mail control:

<input id="emailInput"
   class="form-control"
   email-input
   type="text" ngControl="email"
   [(ngModel)]="developer.email"/>

Next, to add the directive to the list used by the component AddDeveloper directives:

@Component({
  selector: 'dev-add',
  templateUrl: './add_developer.html',
  styles: [`
    input.ng-touched.ng-invalid {
      border: 1px solid red;
    }
  `],
  directives: [FORM_DIRECTIVES, EmailValidator],
  providers: [FORM_PROVIDERS]
})
class AddDeveloper {…}