<!DOCTYPE html>
<html>
  <head>
    <title>Angular 2 + TypeScript + Bootstrap 4</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css" integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi" crossorigin="anonymous">
    <link rel="stylesheet" href="styles.css">
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js@0.6.25?main=browser"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.8"></script>
    <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
    <script src="https://cdn.rawgit.com/angular/angular.io/b3c65a9/public/docs/_examples/_boilerplate/systemjs.config.web.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
    <app></app>
  </body>

</html>
# Angular 2 + TypeScript + Bootstrap 4 (Updated)
## A simple Angular 2 example with ES6, Router, TypeScript, and Form Validation.

Current Version: Angular v2.2.4
Last Updated: Dec 16, 2016
Created By: https://github.com/daveboling

This template contains common use cases and examples similar to those typically
used in Angular 1.

Did I do something bad or forget to update something? **Lemme know!**

*The world is your playground. Why aren't you playing?*
import { Component } from '@angular/core';

@Component({
  selector: 'app',
  templateUrl: './app/app.html'
})
export class AppComponent {}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, Routes } from '@angular/router';
import { ReactiveFormsModule } from '@angular/forms';
import { APP_BASE_HREF } from '@angular/common';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { ExampleFormComponent } from './forms/example-form.component';

const appRoutes: Routes = [
  { path: 'forms', component: ExampleFormComponent },
  { path: '', component: HomeComponent },
];

@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule ,
    RouterModule.forRoot(appRoutes)
  ],
  declarations: [
    AppComponent,
    HomeComponent,
    ExampleFormComponent,
  ],
  providers: [
    { provide: APP_BASE_HREF, useValue: location.pathname }, // This is specifically for Plunker's baseURL: run.plunkr.co
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

<nav class="navbar navbar-light bg-faded">
  <ul class="nav navbar-nav">
    <li class="nav-item">
      <a class="nav-link" routerLink="/">Home</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" routerLink="/forms">Example Form</a>
    </li>
  </ul>
</nav>

<main class='container'>
  <router-outlet></router-outlet>
</main>
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

import { emailValidator, matchingPasswords } from '../../app/validators/validators';

@Component({
  selector: 'example-form',
  templateUrl: './app/forms/example-form.html'
})
export class ExampleFormComponent {
  registrationForm: FormGroup;

  constructor(public fb: FormBuilder) {
    // Example use of FormBuilder, FormGroups, and FormControls
    this.registrationForm = fb.group({
      dob: ['', Validators.required],
      email: ['', Validators.compose([Validators.required,  emailValidator])],
      password: ['', Validators.required],
      confirmPassword: ['', Validators.required],
      firstName: ['', Validators.required],
      lastName: ['', Validators.required]
    }, {validator: matchingPasswords('password', 'confirmPassword')})
    
  }
  
  submitRegistration(value: Object): void {
    console.log(value);
  }

}
<div class='row'>
  <div class='col-xs-12'>
    <h2>Registration Form</h2>
    <form [formGroup]="registrationForm" (ngSubmit)="registrationForm.valid && submitRegistration(registrationForm.value)" novalidate>
      
      <fieldset class="form-group">
        <label for="dob">Date of Birth</label>
        <input type="text" class="form-control" placeholder="Date of Birth" formControlName="dob">
        <div class='form-text error' *ngIf="registrationForm.controls.dob.touched">
          <div *ngIf="registrationForm.controls.dob.hasError('required')">Date of birth is required.</div>
        </div> 
      </fieldset>
      
      <fieldset class="form-group">
        <label for="e-mail">E-Mail</label>
        <input type="email" class="form-control" placeholder="E-mail" formControlName="email">
        <div class="form-text error" *ngIf="registrationForm.controls.email.touched">
          <div *ngIf="registrationForm.controls.email.hasError('required')">Email is required.</div>
          <div *ngIf="registrationForm.controls.email.hasError('invalidEmail')">Email is invalid.</div>
        </div>
      </fieldset>

      <fieldset class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" placeholder="Password" minlength='6' formControlName="password">
        <div class='form-text error' *ngIf="registrationForm.controls.password.touched">
          <div *ngIf="registrationForm.controls.password.hasError('required')">Password is required.</div>
          <div *ngIf="registrationForm.controls.password.hasError('minlength')">Password isn't long enough.</div>
        </div>
      </fieldset>
  
      <fieldset class="form-group">
        <label for="confirm-password">Confirm Password</label>
        <input type="password" class="form-control" placeholder="Confirm Password" formControlName="confirmPassword">
        <div class='form-text error' *ngIf="registrationForm.controls.confirmPassword.touched">
          <div *ngIf="registrationForm.hasError('mismatchedPasswords')">Passwords do not match.</div>
        </div>
      </fieldset>
  
      <fieldset class="form-group">
        <label for="first-name">First Name</label>
        <input type="text" class="form-control" placeholder="First Name" formControlName="firstName">
        <div class='form-text error' *ngIf='registrationForm.controls.firstName.touched'>
          <div *ngIf="registrationForm.controls.firstName.hasError('required')">First name is required.</div>
        </div>
      </fieldset>

      <fieldset class="form-group">
        <label for="last-name">Last Name</label>
        <input type="text" class="form-control" placeholder="Last Name" formControlName="lastName">
        <div class='form-text error' *ngIf='registrationForm.controls.lastName.touched'>
          <div *ngIf="registrationForm.controls.lastName.hasError('required')">Last name is required.</div>
        </div>
      </fieldset> 
      
      <button class='btn btn-primary' type='submit' [disabled]='!registrationForm.valid'>Submit Registration Form</button>
    </form>
    <pre>{{registrationForm.value | json}}</pre>
  </div>
</div>
/*
  Custom validators to use everywhere.
*/

// SINGLE FIELD VALIDATORS
export function emailValidator(control: FormControl): {[key: string]: any} {
  var emailRegexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
  if (control.value && !emailRegexp.test(control.value)) {
    return { invalidEmail: true };
  }
}

// FORM GROUP VALIDATORS
export function matchingPasswords(passwordKey: string, confirmPasswordKey: string) {
  return (group: FormGroup): {[key: string]: any} => {
    let password = group.controls[passwordKey];
    let confirmPassword = group.controls[confirmPasswordKey];
    
    if (password.value !== confirmPassword.value) {
      return {
        mismatchedPasswords: true
      };
    }
  }
}

app {
  .error {
    color: #FF4500;
    font-size: small;
  }
}
import { Component } from '@angular/core';

@Component({
  selector: 'home',
  directives: [],
  template: `<div></div>`
})
export class HomeComponent {}