<!DOCTYPE html>
<html>
  <head>
    <title>Angular 2 Form Builder Custom Validation</title>
    <script src="https://npmcdn.com/systemjs@0.19.6/dist/system.src.js"></script>
    <script src="https://code.angularjs.org/tools/typescript.js"></script>
    <script src="system.config.js"></script>
  </head>
  <body>
    <my-form>Loading...</my-form>
    
    <script>
      System.import('app');
    </script>
  </body>

</html>
import {Component} from '@angular/core';
import {FORM_DIRECTIVES, NgForm, FormBuilder, Control, ControlGroup, Validators} from '@angular/common';
import {CustomValidators} from './custom-validators';

@Component({
  selector: 'my-form',
  templateUrl: 'app/my-form.component.html',
  directives: [FORM_DIRECTIVES],
  styleUrls: ['styles.css']
})
export class MyForm {
  email: Control;
  password: Control;
  group: ControlGroup;
  
  constructor(builder: FormBuilder) {
    this.email = new Control('', 
      Validators.compose([Validators.required, CustomValidators.emailFormat])
    );
    
    this.password = new Control('',
      Validators.compose([Validators.required, Validators.minLength(4)])
    );
    
    this.group = builder.group({
      email: this.email,
      password: this.password
    });
  }
  
  onSubmit() {
    console.log(this.group.value);
  }
}
import {bootstrap} from '@angular/platform-browser-dynamic';
import {MyForm} from './my-form.component.ts';

bootstrap(MyForm);
var angularVersion = '2.0.0-rc.1';

System.config({
  baseUrl: '/',
  paths: {
    'npmcdn:*': 'https://npmcdn.com/*'
  }
});

System.config({
  transpiler: 'typescript', 
  typescriptOptions: { emitDecoratorMetadata: true },

  meta: {
    '*': {
      deps: [ 'zone.js', 'reflect-metadata' ]
    }
  }
});

System.config({
  packageConfigPaths: [
    "npmcdn:@*/*/package.json"
  ],
  
  map: {
    '@angular/core': 'npmcdn:@angular/core@'+angularVersion,
    '@angular/compiler': 'npmcdn:@angular/compiler@'+angularVersion,
    '@angular/common': 'npmcdn:@angular/common@'+angularVersion,
    '@angular/platform-browser': 'npmcdn:@angular/platform-browser@'+angularVersion,
    '@angular/platform-browser-dynamic': 'npmcdn:@angular/platform-browser-dynamic@'+angularVersion,
    '@angular/http': 'npmcdn:@angular/http@'+angularVersion,
    'immutable': 'npmcdn:immutable@3.8.1',
    'rxjs': 'npmcdn:rxjs@5.0.0-beta.6',
    'zone.js': 'npmcdn:zone.js@0.6.12',
    'reflect-metadata': 'npmcdn:reflect-metadata@0.1.3',
    "crypto": "@empty"
  },
  
  packages: {
    'app': {
      defaultExtension: 'ts',
      main: './index.ts'
    }
  }
});
<form [ngFormModel]="group" (ngSubmit)="onSubmit()" novalidate>

  <div>
    <label for="email">Email:</label>
    <input type="email" id="email" [ngFormControl]="email">
    
    <ul *ngIf="email.dirty && !email.valid">
      <li *ngIf="email.hasError('required')">An email is required</li>
    </ul>
  </div>

  <div>
    <label for="password">Password:</label>
    <input type="password" id="password" [ngFormControl]="password">
    
    <ul *ngIf="password.dirty && !password.valid">
      <li *ngIf="password.hasError('required')">A password is required</li>
      <li *ngIf="password.hasError('minlength')">A password needs to have at least 4 characterss</li>
    </ul>
  </div>

  <button type="submit">Register</button>

</form>
.ng-invalid.ng-dirty {
  border-left: 5px solid red;
}
import {Control} from @'angular/common';

export class CustomValidators {
  static emailFormat(control: Control): [[key: string]: boolean] {
    let pattern:RegExp = /\S+@\S+\.\S+/;
    return pattern.test(control.value) ? null : {"emailFormat": true};
  }
}