THE AMAZING

 Amazing SpiderMan slant

by Gerard Sans (@gerardsans)

shield-large Created with Sketch.

Google Developer Expert

Master of Ceremonies

International Speaker

Angular 2 Trainer

Community Leader

800

500

Some days before the conference...

Angular 2

Main Features

  • Simple
  • Fast
  • Works everywhere

ES5 / ES6 / TypeScript

ES6 (ES2015)

- Classes, modules, arrow functions

TypeScript

- Types, annotations

- Better editor support

TypeScript IDE Support

Application Design

Component Tree

source: blog

/home

/users

/about

/users/34

Bootstrap

Bootstrapping

  • Angular Application instantiation
  • Root component (App) 
  • Global Dependencies
    • Router, Http​, Services
    • Global Values
    • Vendor dependencies

index.html

<!DOCTYPE html>
<html>
  <head>
    <script src=".../system.js"></script>
    <script src=".../typescript.js"></script>
    <script src=".../angular2-polyfills.js"></script>
    <script src=".../angular2.dev.js"></script>
    <script src=".../http.js"></script>
    <script src=".../router.js"></script>
    <script src=".../Rx.js"></script>
    <script>System.import('app');</script>
  </head>
  <body>
    <my-app>
      Loading...
    </my-app>
  </body>
</html>

Bootstrap

import {bootstrap} from 'angular2/platform/browser';
import {App} from './app';
import {HTTP_PROVIDERS} from 'angular2/http';
import {UsersService} from './usersService';
...

bootstrap(App, [
  // Global dependencies
  HTTP_PROVIDERS, ROUTER_PROVIDERS,
  // App services
  UsersService, LoginService, MenuService
])
.catch(err => console.error(err));

app.ts

import {Component} from 'angular2/core';

@Component({
  selector: 'my-app',
  template: `...`
})
export class App { 
  constructor() { }
}

Services

/home

/users

loginService.ts

import {Injectable} from 'angular2/core';

@Injectable()
export class LoginService {
  authorised = true;

  authorise(value) {
    this.authorised = value;
  }
}

home.ts

// home.ts
import {LoginService} from './loginService';

@Component({
  template: `
    <h1>Home</h1>
    <input #switch type="checkbox" 
      [checked]="loginService.authorised" 
      (click)="change(switch.checked)">
    <div *ngIf="loginService.authorised">
      <a>Today's best superheroe</a>
    </div>`
})
export class Home { 
  constructor(private loginService:LoginService) { }

  change($event){
    this.loginService.authorise($event);
  }
}

/users

users.json

[{
  "id": 34,
  "username": "spiderman",
  "roles": ["admin", "user"]
}, {
  "id": 67,
  "username": "batman",
  "roles": ["user"]
}]

usersService.ts

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/retryWhen';

@Injectable()
export class UsersService {
  constructor(private http:Http) { }
  
  get(){
    return this.http.get('api/users.json')
      .map(response => response.json())
      .retryWhen(errors => errors.delay(2000));
  }
}

usersList.ts

import {Component} from 'angular2/core';
import {UsersService} from './usersService';

@Component({
  selector: 'users',
  template: `
    <h1>Users</h1>
    <table class="table">
      <tr *ngFor="#user of users">
        <td>{{user.username}}</td>
      </tr>
    </table>`
})
export class UsersList { 
  constructor(private service:UsersService){
    service.get().subscribe(users => this.users = users);
  }
}

Setup Router

/home

/users

Location Strategies

  • HashLocationStrategy
    • Eg: #/home, #/users/34
  • PathLocationStrategy (default)

Bootstrap

import {ROUTER_PROVIDERS} from 'angular2/router';
import {LocationStrategy, HashLocationStrategy} from 'angular2/router';

bootstrap(App, [
  ROUTER_PROVIDERS,
  provide(LocationStrategy, { useClass: HashLocationStrategy }),
])

/home

/users

/about

/users/34

Defining Routes

import {RouteConfig} from 'angular2/router';
import {Home} from './home';
import {Users} from './users';
import {AboutLazyLoader} from './aboutLazyLoader';

@RouteConfig([
  { path: '/home', component: Home, name: 'Home' },
  { path: '/users/...', component: Users, name: 'Users' },
  { path: '/about', loader: AboutLazyLoader , name: 'AboutLazyLoad' },
  { path: '/**', redirectTo: ['Home'] }
])
export class App { }

Outlets

import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';

@Component({
  selector: 'my-app',
  template: `
    <nav>...</nav>
    <div>
      <router-outlet></router-outlet>
    </div>`,
  directives: [ROUTER_DIRECTIVES]
})
@RouteConfig(...)
export class App { }

Users

/

UsersList

#/users, #/users/

:id

UserDetails

#/users/34

Child Routes

  • Parent uses special mark
    • /users/...
  • Child defines its own routes
  • Always define a default route
    • useAsDefault: true

Child Routes

import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {UsersList} from './usersList';
import {UserDetails} from './userDetails';

@Component({
  selector: 'users',
  template: `<router-outlet></router-outlet>`,
  directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
  { path: '/', component: UsersList, name: 'UsersList', useAsDefault: true },
  { path: ':id', component: UserDetails, name: 'UserDetails'  },
])
export class Users { }

Navigation

  • Using regular links with hash
    • #/home
  • Using routerLink directive uses link DSL
    • ['Home']
    • ['/Users/UserDetails', {id: 34}]
  • Programatically
    • router.navigate(['Home']);
    • router.navigateByUrl('/home');

routerLink

<!-- Top Routes -->
<a [routerLink]="['Home']">Home</a>
<a [routerLink]="['Users']">Users</a>
<a [routerLink]="['AboutLazyLoad']">About</a>

<!-- Nested Routes -->
<a [routerLink]="['/Users/UsersList']">Users</a>
<a [routerLink]="['/Users/UserDetails', {id: 34}]">Spiderman</a>
<a [routerLink]="['/Users/UserDetails', {id: 34, flag: true}]">Spiderman</a>

<!-- Relative Routes -->
<a [routerLink]="['./UsersList']">Users</a>
<a [routerLink]="['./UserDetails', {id: 34}]">Spiderman</a>
<a [routerLink]="['./UserDetails', {id: 34, flag: true}]">Spiderman</a>

Accessing Parameters

/* 
  { path: ':id', component: UserDetails, name: 'UserDetails' }
*/

import {RouteParams} from 'angular2/router';

@Component({
  selector: 'user-details'
})
export class UserDetails { 
  constructor(private params: RouteParams){
    let id = params.get('id');
  }
}

Navigation Features

Save changes

@Component({
  selector: 'user-details'
})
export class UserDetails implements CanDeactivate { 

  routerCanDeactivate() {
    return new Promise(resolve => 
      resolve(window.confirm('Do you want to continue?'))
    );
  }
}

Block Access

@Component({
  selector: 'user-details'
})
@CanActivate(next => {
  let injector = appInjector();
  let service = injector.get(LoginService);
    return new Promise(resolve => { 
      resolve(service.authorised);
    });
})
export class UserDetails { }

 

CanDeactivate
 
CanDeactivate
@CanActivate
@CanActivate
OnDeactivate
OnDeactivate
OnActivate
OnActivate

 

  Navigation from users list to user details

  1. UsersList.routerCanDeactivate()
  2. UserDetails.@CanActivate()
  3. UsersList.routerOnDeactivate()
  4. UserDetails.routerOnActivate()

Lazy Loading

Setup

@RouteConfig([
  { path: '/about', loader: AboutLazyLoader , name: 'AboutLazyLoad' }
])
export class App { }

//aboutLazyLoader.ts
export function AboutLazyLoader(){
  return System.import('./src/aboutLazyLoad.ts')
    .then(module => module.AboutLazyLoad);
}

//aboutLazyLoad.ts
@Component({
  selector: 'about',
  template: `<h1>About</h1>`
})
export class AboutLazyLoad { }

Demo

Features

  • PathLocationStrategy setup
  • Nested routes with parameters
  • Block leaving route
  • Block route access
  • SVG loading spinner
  • Active menu css style fix

ng-conf - May 4-6th

@ngconf​

AngularConnect - Sept 27-28th

@AngularConnect

Danke schön!