Angular 2 Master Class – Jump Start Tutorial



Angular 2 Master Class – Jump Start Tutorial

3 7


angular2-master-class-jump-start-slides

Angular 2 Master Class: Jump Start slide deck

On Github thoughtram / angular2-master-class-jump-start-slides

Angular 2 Master Class

Jump Start Tutorial

Introduction

Angular 2?

  • The next major version of the Angular framework
  • Rather a development platform than just a framework
  • Mobile First, future ready, powered by open source community
  • Faster, better tooling, more flexible, ...

Framework to Platform

Performance

Performance

Performance

Languages

Languages

Languages

Languages

Useful Resources

ES2015/TypeScript Basics

Classes

Syntactic sugar for JavaScript prototypes introduced in ES2015.

class Car {

  manufacturer:string;

  constructor(manufacturer:string = 'BMW') {
    this.manufacturer = manufacturer;
  }

  drive(miles:number) {}
}

let bmw = new Car();

Classes

Syntactic sugar for JavaScript prototypes introduced in ES2015.

class Car {

  manufacturer:string;

  constructor(manufacturer:string = 'BMW') {
    this.manufacturer = manufacturer;
  }

  drive(miles:number) {}
}

let bmw = new Car();

Classes

Syntactic sugar for JavaScript prototypes introduced in ES2015.

class Car {

  manufacturer:string;

  constructor(manufacturer:string = 'BMW') {
    this.manufacturer = manufacturer;
  }

  drive(miles:number) {}
}

let bmw = new Car();

Classes

Syntactic sugar for JavaScript prototypes introduced in ES2015.

class Car {

  manufacturer:string;

  constructor(manufacturer:string = 'BMW') {
    this.manufacturer = manufacturer;
  }

  drive(miles:number) {}
}

let bmw = new Car();

Classes

Syntactic sugar for JavaScript prototypes introduced in ES2015.

class Car {

  manufacturer:string;

  constructor(manufacturer:string = 'BMW') {
    this.manufacturer = manufacturer;
  }

  drive(miles:number) {}
}

let bmw = new Car();
class Car { ... }

class Convertible extends Car {

}

let cabrio = new Convertible();
class Car { ... }

class Convertible extends Car {

}

let cabrio = new Convertible();

Modules

ES2015 brings a module system to the table that enables us to write modular code.

// Car.js

export class Car { ... }

export class Convertible extends Car {
  ...
}

Modules

ES2015 brings a module system to the table that enables us to write modular code.

// Car.js

export class Car { ... }

export class Convertible extends Car {
  ...
}

Modules

ES2015 brings a module system to the table that enables us to write modular code.

// App.js

import {Car, Convertible} from 'Car';

let bmw = new Car();
let cabrio = new Convertible();

Modules

ES2015 brings a module system to the table that enables us to write modular code.

// App.js

import {Car, Convertible} from 'Car';

let bmw = new Car();
let cabrio = new Convertible();

Modules

ES2015 brings a module system to the table that enables us to write modular code.

// App.js

import {Car, Convertible} from 'Car';

let bmw = new Car();
let cabrio = new Convertible();

Type Annotations

Type annotations provide optional static typing. Applied using : T syntax

var height:number = 6;
var isDone:boolean = true;
var name:string = 'thoughtram';

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

function add(x: number, y: number): number {
  return x+y;
}

Type Annotations

Type annotations provide optional static typing. Applied using : T syntax

var height:number = 6;
var isDone:boolean = true;
var name:string = 'thoughtram';

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

function add(x: number, y: number): number {
  return x+y;
}

Type Annotations

Type annotations provide optional static typing. Applied using : T syntax

var height:number = 6;
var isDone:boolean = true;
var name:string = 'thoughtram';

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

function add(x: number, y: number): number {
  return x+y;
}

Type Annotations

Type annotations provide optional static typing. Applied using : T syntax

var height:number = 6;
var isDone:boolean = true;
var name:string = 'thoughtram';

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

function add(x: number, y: number): number {
  return x+y;
}

Type Annotations

Type annotations provide optional static typing. Applied using : T syntax

var height:number = 6;
var isDone:boolean = true;
var name:string = 'thoughtram';

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

function add(x: number, y: number): number {
  return x+y;
}

Type Annotations

Type annotations provide optional static typing. Applied using : T syntax

var height:number = 6;
var isDone:boolean = true;
var name:string = 'thoughtram';

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

function add(x: number, y: number): number {
  return x+y;
}

Bootstrapping an app







export class ContactsAppComponent {}
import { Component } from '@angular/core';

@Component({
  selector: 'trm-contacts-app',
  template: 'Hello World!',
  styleUrls: ['contacts.component.css']
})
export class ContactsAppComponent {}

Every component is part of an NgModule

import { NgModule } from '@angular/core';



@NgModule({



})
export class ContactsModule {}
import { NgModule } from '@angular/core';

import { ContactsAppComponent } from './contacts.component';

@NgModule({

  declarations: [ContactsAppComponent],
  bootstrap: [ContactsAppComponent]
})
export class ContactsModule {}
import { NgModule } from '@angular/core';

import { ContactsAppComponent } from './contacts.component';

@NgModule({

  declarations: [ContactsAppComponent],
  bootstrap: [ContactsAppComponent]
})
export class ContactsModule {}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ContactsAppComponent } from './contacts.component';

@NgModule({
  imports: [BrowserModule],
  declarations: [ContactsAppComponent],
  bootstrap: [ContactsAppComponent]
})
export class ContactsModule {}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ContactsAppComponent } from './contacts.component';

@NgModule({
  imports: [BrowserModule],
  declarations: [ContactsAppComponent],
  bootstrap: [ContactsAppComponent]
})
export class ContactsModule {}

How to bootstrap this module?

import { platformBrowserDynamic } from '@angular/core';
import { ContactsModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(ContactsModule);
import { platformBrowserDynamic } from '@angular/core';
import { ContactsModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(ContactsModule);
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Contacts</title>
  </head>
  <body>



    <script src="..."></script>
  </body>
</html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Contacts</title>
  </head>
  <body>

    <trm-contacts-app>Loading...</trm-contacts-app>

    <script src="..."></script>
  </body>
</html>

Bootstrapping Tasks

Angular performs the following tasks to bootstrap an application (simplified):

Upgrades located DOM element into Angular component Creates injector for the application Creates (emulated) Shadow DOM on component's host element Instantiates specified component Performs change detection

Demo: Bootstrapping an app

Using directives

Directive Dependencies

Directives/Components are added to an NgModule's declarations property.


@NgModule({
  imports: [BrowserModule],
  declarations: [ContactsAppComponent],
  bootstrap: [ContactsAppComponent]
})
export class ContactsModule {}

Directive Dependencies

Directives/Components are added to an NgModule's declarations property.

import { ContactsHeaderComponent } from './contacts-header';

@NgModule({
  imports: [BrowserModule],
  declarations: [ContactsAppComponent, ContactsHeaderComponent],
  bootstrap: [ContactsAppComponent]
})
export class ContactsModule {}

Directive Dependencies

Directives/Components are added to an NgModule's declarations property.

import { ContactsHeaderComponent } from './contacts-header';

@NgModule({
  imports: [BrowserModule],
  declarations: [ContactsAppComponent, ContactsHeaderComponent],
  bootstrap: [ContactsAppComponent]
})
export class ContactsModule {}

Directive Dependencies

Directives/Components can then be used in components throughout that module.

@Component({
  selector: 'trm-contacts-app',
  template: 'Hello World!',
  styleUrls: ['contacts.component.css']
})
export class ContactsAppComponent {}

Directive Dependencies

Directives/Components can then be used in components throughout that module.

@Component({
  selector: 'trm-contacts-app',
  template: '<trm-contacts-header></trm-contacts-header>',
  styleUrls: ['contacts.component.css']
})
export class ContactsAppComponent {}

Exercise 1: Using Directives

Tip: Don't forget to git commit your solution

Displaying Data

We can bind data to elements in HTML templates and Angular automatically updates the UI as data changes.

@Component({
  selector: 'trm-contacts-app',
  template: 'Hello {{name}}'
})
export class ContactsAppComponent {
  name = 'Angular 2';
}
@Component({
  selector: 'trm-contacts-app',
  template: 'Hello {{name}}'
})
export class ContactsAppComponent {
  name = 'Angular 2';
}

Let's display our first contact!

Defining a Contact

To enable type safety during compilation, we can define interfaces using the interface key word

interface Contact {
  id: Number;
  name: string;
  email: string;
  phone: string;
  ...
}
import { Contact } from './models/contact';

@Component(...)
export class ContactsAppComponent {







}
import { Contact } from './models/contact';

@Component(...)
export class ContactsAppComponent {
  contact: Contact = {
    id: 1,
    name: 'Christoph',
    email: 'chris@burdorf.io',
    image: 'path/to/image',
    ...
  }
}
@Component({
  selector: 'trm-contacts-app',
  template: `
    
{{contact.name}}
` }) export class ContactsAppComponent { ... }
@Component({
  selector: 'trm-contacts-app',
  template: `
    <div>
      <img [src]="contact.image">
      <span>
        {{contact.name}}
      </span>
    </div>
  `
})
export class ContactsAppComponent {
  ...
}

Exercise 2: Display first contact

Tip: Don't forget to git commit your solution

Property Binding Explained

Props and Attrs

Attributes are always strings and only set up the initial value

<input value="Christoph">
<img src="path/to/img.png">

Props and Attrs

Properties can be any value and changed imperatively at run-time

var input = document.querySelector('input');
input.value; // 'Christoph'

var img = document.querySelector('img');
img.src; // 'path/to/img.png'

Props and Attrs

Properties and attributes values aren't always reflected automatically

var input = document.querySelector('input');
input.value; // 'Christoph'

input.value = 'Pascal';
input.getAttribute('value'); // 'Christoph'

Props and Attrs

Properties and attributes values aren't always reflected automatically

var input = document.querySelector('input');
input.value; // 'Christoph'

input.value = 'Pascal';
input.getAttribute('value'); // 'Christoph'

Property Binding

Angular 2 binds to element properties instead of attributes in order to work with any element

[] - syntax is the declarative way of writing to a property

Creating a list

export const CONTACTS: Contact[] = [
  { id: 1, firstname: 'Christoph', ...},
  { id: 2, firstname: 'Pascal', ...},
  { id: 3, firstname: 'Julie', ...},
  { id: 4, firstname: 'Igor', ...},
  ...
];
import { CONTACTS } from './data/contact-data';

@Component({
  selector: 'trm-contacts-app',
  template: `
    
` }) export class ContactsAppComponent { contacts: Contact[] = CONTACTS; }
import { CONTACTS } from './data/contact-data';

@Component({
  selector: 'trm-contacts-app',
  template: `
    <ul>
      <li>
        <!-- each contact goes here -->
      </li>
    </ul>
  `
})
export class ContactsAppComponent {
  contacts: Contact[] = CONTACTS;
}

Lists with ngFor

The NgFor directive instantiates a template once per item from an iterable.

<li *ngFor="let item of items">...</li>
  • * - Indicates template directive
  • items - Expression evaluating to collection
  • let item - Local variable for each iterator
@Component({
  selector: 'trm-contacts-app',
  template: `
    <ul>
      <li>

        <!-- each contact goes here -->


      </li>
    </ul>
  `
})
export class ContactsAppComponent {
  ...
}
@Component({
  selector: 'trm-contacts-app',
  template: `
    <ul>
      <li *ngFor="let contact of contacts">
        <img [src]="contact.image">
        <span>
          {{contact.name}}
        </span>
      </li>
    </ul>
  `
})
export class ContactsAppComponent {
  ...
}

Demo →

Exercise 3: Display list of contacts

Tip: Don't forget to git commit your solution

Services and DI

Services

We can create services in Angular using ES2015 classes.

import { Injectable } from '@angular/core';

@Injectable()
export class ContactsService {

  private contacts: Contact[] = CONTACTS;

  getContacts() {
    return this.contacts;
  }
}

Configuring the Injector

An NgModule's providers accepts a list of providers to configure the injector.

@NgModule({
  ...
  providers: [
    { provide: ContactsService, useClass: ContactsService }
  ]
})
export class ContactsModule {}

Configuring the Injector

An NgModule's providers accepts a list of providers to configure the injector.

@NgModule({
  ...
  providers: [
    { provide: ContactsService, useClass: ContactsService }
  ]
})
export class ContactsModule {}

Configuring the Injector

An NgModule's providers accepts a list of providers to configure the injector.

@NgModule({
  ...
  providers: [
    ContactsService
  ]
})
export class ContactsModule {}

Injecting Dependencies

Dependencies can be injected using TypeScript type annotations.

@Component(...)
export class ContactsAppComponent {

  contacts: Contact[];

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

Injecting Dependencies

Dependencies can be injected using TypeScript type annotations.

@Component(...)
export class ContactsAppComponent {

  contacts: Contact[];

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

Lifecycle Hooks

Directives and Components have lifecycle hooks. Some of them are:

  • ngOnInit - Initializes directive
  • ngOnChanges - Respond after Angular sets data-bound property
  • ngDoCheck - Custom change detection
  • ngOnDestroy - Cleanup before Angular destroys directive

OnInit Lifecycle

We use ngOnInit to do initialization work in the component.

import { OnInit } from '@angular/core';
...
export class ContactsAppComponent implements OnInit {

  contacts: Contact[];

  constructor(private contactsService: ContactsService) {}

  ngOnInit() {
    this.contacts = this.contactsService.getContacts();
  }
}

OnInit Lifecycle

We use ngOnInit to do initialization work in the component.

import { OnInit } from '@angular/core';
...
export class ContactsAppComponent implements OnInit {

  contacts: Contact[];

  constructor(private contactsService: ContactsService) {}

  ngOnInit() {
    this.contacts = this.contactsService.getContacts();
  }
}

Keep in mind

  • We use the token (or type) to ask for a dependency instance
  • But a provider defines what and how an object is being created for that token (or type)

Exercise 4: Services and DI

Tip: Don't forget to git commit your solution

Understanding the injector tree

Component Routing

Components we have

Our application consists of three components:

  • ContactsAppComponent - The root component that is being bootstrapped
  • ContactsListComponent - A component to list contacts by provided data
  • ContactDetailComponent - A component to show a contact's details

Defining Routes

We define routes as a collection of Route objects that have a path and a component

import { ContactsListComponent } from './contacts-list';

export const ContactsAppRoutes = [
  { path: '', component: ContactsListComponent }
]

Defining Routes

We define routes as a collection of Route objects that have a path and a component

import { ContactsListComponent } from './contacts-list';

export const ContactsAppRoutes = [
  { path: '', component: ContactsListComponent }
]

Adding Router Module

RouterModule.forRoot() configures routes for the root module of our app.

import { RouterModule } from '@angular/router';
import { ContactsAppRoutes } from './app.routes';

@NgModule({
  imports: [
    ...
    RouterModule.forRoot(ContactsAppRoutes)
  ]
})
export class ContactsModule {}

Adding Router Module

RouterModule.forRoot() configures routes for the root module of our app.

import { RouterModule } from '@angular/router';
import { ContactsAppRoutes } from './app.routes';

@NgModule({
  imports: [
    ...
    RouterModule.forRoot(ContactsAppRoutes)
  ]
})
export class ContactsModule {}

Displaying Components

<router-outlet> directive specifies a viewport where components should be loaded into.

@Component({
  selector: 'trm-contacts-app',
  template: '...',
  styleUrls: ['contacts.component.css']
})
class ContactsAppComponent {...}

Displaying Components

<router-outlet> directive specifies a viewport where components should be loaded into.

@Component({
  selector: 'trm-contacts-app',
  template: '<router-outlet></router-outlet>',
  styleUrls: ['contacts.component.css']
})
class ContactsAppComponent {...}

Exercise 5: Route to first component

Tip: Don't forget to git commit your solution

Links with RouterLink

RouterLink lets link to a specific part of our app.

<li *ngFor="let contact of contacts">

    <img src="{{contact.image}}">
    <span>
      {{contact.name}}
    </span>

</li>

Links with RouterLink

RouterLink lets link to a specific part of our app.

<li *ngFor="let contact of contacts">
  <a [routerLink]="['/contact', contact.id]">
    <img src="{{contact.image}}">
    <span>
      {{contact.name}}
    </span>
  </a>
</li>

Links with RouterLink

RouterLink lets link to a specific part of our app.

<li *ngFor="let contact of contacts">
  <a [routerLink]="['/contact', contact.id]">
    <img src="{{contact.image}}">
    <span>
      {{contact.name}}
    </span>
  </a>
</li>
import { ContactsListComponent } from './contacts-list';


export const ContactsAppRoutes = [
  { path: '', component: ContactsListComponent }

]
import { ContactsListComponent } from './contacts-list';
import { ContactsDetailComponent } from './contacts-detail';

export const ContactsAppRoutes = [
  { path: '', component: ContactsListComponent },
  { path: 'contact/:id', component: ContactsDetailComponent }
]
import { ContactsListComponent } from './contacts-list';
import { ContactsDetailComponent } from './contacts-detail';

export const ContactsAppRoutes = [
  { path: '', component: ContactsListComponent },
  { path: 'contact/:id', component: ContactsDetailComponent }
]

Retrieving parameters

We can access a snapshot of a route state via ActivatedRoute.snapshot

import { ActivatedRoute } from '@angular/router';

@Component(...)
class ContactsDetailComponent {

  constructor(route: ActivatedRoute) {
    let id = this.route.snapshot.params['id'];
    this.contact = this.contactsService
                       .getContact(id);
  }
}

Retrieving parameters

We can access a snapshot of a route state via ActivatedRoute.snapshot

import { ActivatedRoute } from '@angular/router';

@Component(...)
class ContactsDetailComponent {

  constructor(route: ActivatedRoute) {
    let id = this.route.snapshot.params['id'];
    this.contact = this.contactsService
                       .getContact(id)
  }
}

Exercise 6: Using RouteParams

Tip: Don't forget to git commit your solution

Fetching Data

Http in Angular 2

Angular 2 introduces a completely redesigned Http layer based on Observables.

  • Event-driven stream of notifications
  • Enables functional programming structures
  • Allows to transform to promises if needed

Adding Http Module

HttpModule configures the injector for http dependencies.

import { HttpModule } from '@angular/http';
import 'rxjs/add/operator/map';

@NgModule({
  imports: [
    ...
    HttpModule
  ]
})
export class ContactsModule {...}

Adding Http Module

HttpModule configures the injector for http dependencies.

import { HttpModule } from '@angular/http';
import 'rxjs/add/operator/map';

@NgModule({
  imports: [
    ...
    HttpModule
  ]
})
export class ContactsModule {...}

Adding Http Module

HttpModule configures the injector for http dependencies.

import { HttpModule } from '@angular/http';
import 'rxjs/add/operator/map';

@NgModule({
  imports: [
    ...
    HttpModule
  ]
})
export class ContactsModule {...}

Http Service

Http is an injectable class with methods to perform http requests.

import { Http } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
class ContactsService {
  constructor(private http: Http) {

  }
}

Http Service

Http is an injectable class with methods to perform http requests.

import { Http } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
class ContactsService {
  constructor(private http: Http) {

  }
}

Http Service

Http is an injectable class with methods to perform http requests.

import { Http } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
class ContactsService {
  constructor(private http: Http) {

  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map((res) => { return res.json(); })
      .map((data) => { return data.items; });
  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map((res) => { return res.json(); })
      .map((data) => { return data.items; });
  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map((res) => { return res.json(); })
      .map((data) => { return data.items; });
  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map(res => res.json())
      .map(data => data.items);
  }
}

Subscribing to Http

We can subscribe to Observables using .subscribe()

@Component(...)
class ContactsListComponent {

  constructor(contactsService: ContactsService) {
    contactsService.getContacts()
      .subscribe(contacts => this.contacts = contacts);
  }

}

Subscribing to Http

We can subscribe to Observables using .subscribe()

@Component(...)
class ContactsListComponent {

  constructor(contactsService: ContactsService) {
    contactsService.getContacts()
      .subscribe(contacts => this.contacts = contacts);
  }

}

Safe Navigation Operator

Angular throws a null reference error when null objects are accessed:

The null contact's name is {{nullContact.name}}

Results in:

TypeError: Cannot read property 'name' of null in [null]

Safe Navigation Operator

The "safe navigation" operator (?.) is a convenient way to guard against nulls in property paths

The null contact's name is {{nullContact?.name}}

Safe Navigation Operator

The "safe navigation" operator (?.) is a convenient way to guard against nulls in property paths

The null contact's name is {{nullContact?.name}}

Exercise 7: Fetching data using Http

Tip: Don't forget to git commit your solution

Two-way Data Binding

NgModel Directive

NgModel implements two-way data binding by providing and combining property and event bindings.

<input [(ngModel)]="name">

Hello, {{name}}!

Or, the canonical version:

<input bindon-ngModel="name">

Hello, {{name}}!

Two-way Binding can be implemented like this:

<input [value]="name"
  (input)="name=$event.target.value">

ngModel hides repetitive expressions:

<input [ngModel]="name"
  (ngModelChange)="name=$event">

Shorthand syntax:

<input [(ngModel)]="name">

Two-way Binding can be implemented like this:

<input [value]="name"
  (input)="name=$event.target.value">

ngModel hides repetitive expressions:

<input [ngModel]="name"
  (ngModelChange)="name=$event">

Shorthand syntax:

<input [(ngModel)]="name">

Adding FormsModule

To use NgModel we need to add FormsModule to our application.

import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    ...
    FormsModule
  ]
})
export class ContactsModule {}

Simple Forms

Using ngModel we can already create simple forms.

<form>

  <label>Firstname:</label>
  <input [(ngModel)]="contact.firstname">

  <label>Lastname:</label>
  <input [(ngModel)]="contact.lastname">
  ...
</form>

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/${contact.id}`;

    return this.http.put(url, contact);
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/${contact.id}`;

    return this.http.put(url, contact);
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/${contact.id}`;

    return this.http.put(url, contact);
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/${contact.id}`;

    return this.http.put(url, contact);
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/${contact.id}`;

    return this.http.put(url, contact);
  }
}

Exercise 8: Sending data using Http

Tip: Don't forget to git commit your solution

Using Async Pipe

Pipes

A pipe takes in data as input and transforms it to a desired output.

@Component({
  selector: 'trm-contacts-detail',
  template: `
    ...
    Birthday:
    {{contact?.dateOfBirth | date}}
  `
})
class ContactsDetailComponent { }

Pipes

A pipe takes in data as input and transforms it to a desired output.

@Component({
  selector: 'trm-contacts-detail',
  template: `
    ...
    <span>Birthday:</span>
    <span>{{contact?.dateOfBirth | date}}</span>
  `
})
class ContactsDetailComponent { }

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

import { Observable } from 'rxjs/Observable';

@Component(...)
class ContactsListComponent {

  contacts: Observable<Array<Contact>>;

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

import { Observable } from 'rxjs/Observable';

@Component(...)
class ContactsListComponent {

  contacts: Observable<Array<Contact>>;

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

@Component({
  template: `
    <ul>
      <li *ngFor="let contact of contacts | async">
        ...
      </li>
    </ul>
  `
})
class ContactsListComponent {...}

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

@Component({
  template: `
    <ul>
      <li *ngFor="let contact of contacts | async">
        ...
      </li>
    </ul>
  `
})
class ContactsListComponent {...}

Exercise 9: Async Pipe

Tip: Don't forget to git commit your solution

Angular 2 Master Class Jump Start Tutorial