New Data

Architecture

in Angular 2

slides.com/gerardsans | @gerardsans

Google Developer Expert

Master of Ceremonies

International Speaker

Angular 2 Trainer

Community Leader

800

500

Angular 2

Features

  • Latest Web Standards
  • Simple
  • Lightning fast
  • Works everywhere

ES5, ES6 and TypeScript

ES5 / ES6 / TypeScript

ES6 (ES2015)

  • Classes, modules, arrow functions

TypeScript

  • Types, decorators, generics, interfaces
  • Great editor support

Angular 2 Tooling

Angular 2 Tooling

Bootstrap

Overview

  • Angular Application instantiation
  • Root Module (AppModule) 
  • Application Context
    • RouterModule, HttpModule​
    • Components, directives, pipes
    • Services

index.html

<!DOCTYPE html>
<html>
  <head>
    <!-- Polyfill(s) for older browsers -->
    <script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js">
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="systemjs.config.js"></script>
    <script>System.import('app');</script>
  </head>
  <body>
    <my-app>
      Loading...
    </my-app>
  </body>
</html>

main.ts

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

platformBrowserDynamic().bootstrapModule(AppModule)

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { App } from './app.component';

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

app.component.ts

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

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

Try it out!

Components

Components Tree

source: blog

Component

  • @Component decorator
  • Communications
    • Inputs, @Input
    • Outputs, @Output
  • Component Lifecycle Hooks
  • Host element interaction

Component

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

@Component({
  selector:    'home', // <home></home>
  styles:      [`h1 { color: red }`],
  template:    `<h1>Home</h1>`
})
export class Home { ... }

Lifecycle Hooks

import { Component, OnChanges, OnInit, OnDestroy } from '@angular/core';

@Component()
export class myComponent implements OnChanges, OnInit, OnDestroy { 
  /* 1 */ constructor() { }

  // called when an input or output binding changes
  /* 2 */ ngOnChanges(changes) { }   
  
  // after child initialisation
  /* 3 */ ngOnInit() { } 
  
  // just before is destroyed
  /* 4 */ ngOnDestroy() { } 
}

Reactive Extensions

RxJS 5

beta12

1

2

3

Stream

Observable

//Observable constructor
let simple$ = Rx.Observable.create(observer => {
  try {
    //pushing values
    observer.next(1);
    observer.next(2);
    observer.next(3);

    //complete stream
    observer.complete();
  }
  catch(e) {
    //error handling
    observer.error(e);
  }
});

Subscribe

/*
a$ ---1---2---3|
*/

let a$ = Rx.Observable.of(1,2,3);

let subscription = a$.subscribe({
  next:  x => console.log(x),
  error: x => console.log('#'),
  complete: () => console.log('|')
});

Unsubscribe

let subscription = twits$.subscribe(
  twit => feed.push(twit),
  error => console.log(error),
  () => console.log('done')
);

setTimeout(() => subscription.unsubscribe(), 5000);

What's your guess?

Rx.Observable.of(1)
  .subscribe({
    next: x => console.log(x),
    complete: () => console.log('3') 
  });
console.log('2');

// a) 1 2 3
// b) 2 1 3
// c) 1 3 2
// d) 3 2 1

Schedulers

Observable.of(1)
  .subscribeOn(Rx.Scheduler.async)
  .subscribe({
    next: (x) => console.log(x),
    complete: () => console.log('3') 
  });
console.log('2');

// a) 1 2 3
// b) 2 1 3
// c) 1 3 2
// d) 3 2 1

RxJS 5 in Angular2

  • Asynchronous processing
  • Http
  • Forms: controls, validation
  • Component events
    • EventEmitter

Why Observables?

  • Flexible: sync or async
  • Powerful operators
  • Less code

Templating

Template Syntax

Syntax Binding type
<h1>{{title}}</h1>
<input [value]="firstName">
<li [class.active]="isActive"></li>
<div [style.width.px]="mySize">
Interpolation
Property
Class
Style
<button (click)="onClick($event)"> Event
[(ngModel)]="data.value" Two-way

Component

Architecture

Unidirectional Data Flow

source: blog

Data Services

Http Module

  • Primary protocol for client/server communications
  • Implements XMLHttpRequest (XHR) and JSONP
  • Http methods:  GET, POST, PUT, DELETE, PATCH and HEAD

Creating a Http Service

// app.module.ts
import { HttpModule }  from '@angular/http';
@NgModule({ 
  imports: [HttpModule], ... 
})
export class AppModule {}

Creating a Http Service

// src/users.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

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

Consuming a Http Service

import { Component } from '@angular/core';
import { UsersService } from './users.service';

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

Why use Data Services?

  • Singleton instance
  • Share state between Components
  • Leverage Observables

Try it out!

State Management

Dan Abramov

@gaearon

Redux

Main Features

  • Unidirectional data flow
  • Single Immutable State
  • New states are created without side-effects

Unidirectional data flow

Components Tree

<root>
  <add-todo>
    <input><button>Add todo</button>
  </add-todo>
  <todo-list>
    <ul>
      <todo id="0" completed="false"><li>buy milk</li></todo>
    </ul>
  </todo-list>
  <filters>
    Show: <filter-link><a>All</a><filter-link> ... 
  </filters>
</root>

Redux Setup

import { App } from './app';
import { createStore } from 'redux';
 
const appStore = createStore(rootReducer);

@NgModule({
  imports: [ BrowserModule ],
  declarations: [
    App, ...APP_DECLARATIONS
  ],
  providers: [
    { provide: 'AppStore', useValue: appStore },
    TodoActions 
  ],
  bootstrap: [ App ]
})
export class AppModule { }

platformBrowserDynamic().bootstrapModule(AppModule);

Adding a new Todo

  • Component subscribe to the Store
  • Component dispatches ADD_TODO action
  • Store executes rootReducer
  • Store notifies Component
  • View updates

Subscribing to the Store

@Component({
  template: 
      `<todo *ngFor="let todo of todos">{{todo.text}}</todo>`
})

export class TodoList implements OnDestroy {
  constructor(@Inject('AppStore') private appStore: AppStore){
    this.unsubscribe = this.appStore.subscribe(() => {
      let state = this.appStore.getState();
      this.todos = state.todos;
    });
  }
  private ngOnDestroy(){
    this.unsubscribe();
  }
}

ADD_TODO Action

// add new todo item
{
  type: ADD_TODO,
  id: 1,
  text: "learn redux",
  completed: false
}

todos Reducer

const todos = (state = [], action) => {
  switch (action.type) {
    case TodoActions.ADD_TODO: 
      return state.concat({ 
          id: action.id,
          text: action.text,
          completed: action.completed });
    default: return state;
  }
}

currentFilter Reducer

const currentFilter = (state = 'SHOW_ALL', action) => {
  switch (action.type) {
    case 'SET_CURRENT_FILTER':
      return action.filter
    default: return state
  }
}

rootReducer

import { combineReducers } from 'redux'

export const rootReducer = combineReducers({
  todos: todos,
  currentFilter: currentFilter
});

New State

{
  todos: [{
    id: 1,
    text: "learn redux",
    completed: false
  }],
  currentFilter: 'SHOW_ALL'
}

Stateless Todo Component

// <todo id="1" completed="true">buy milk</todo>
@Component({
  inputs: ['id', 'completed'],
  template: `
    <li (click)="onTodoClick(id)"
      [style.textDecoration]="completed?'line-through':'none'">
      <ng-content></ng-content>
    </li>`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class Todo { 
  constructor(
    @Inject('AppStore') private appStore: AppStore, 
    private todoActions: TodoActions){ }

  private onTodoClick(id){
    this.appStore.dispatch(this.todoActions.toggleTodo(id));
  }
}

Change Detection

source: blog

Change Detection

source: blog

Why use Redux?

  • Simplified Development
  • Avoids complex dependencies
  • Great Performance
  • Developer Experience (Dev Tools)

Try it out!

GraphQL

GraphQL

  • Developed at Facebook 2012
  • Help Mobile Native Teams 
  • Open sourced 2015 as a specification
  • Framework agnostic

GraphQL Server

source: blog

Why use GraphQL?

  • Super fast
  • De-coupled from storage
  • Declarative
  • Validated and structured
  • Facilitates Collaboration

Demo

Try it out!

What should I use?

Your Options

Thanks!

New Data Architecture in Angular 2

By Gerard Sans

New Data Architecture in Angular 2

Angular 2 introduces a new Data Architecture based on Reactive Programming using RxJS 5. We are really excited to see the new approach using Observable sequences aka Observables. We will cover a basic introduction of Angular 2 followed by RxJS 5 and the new HTTP API covering some basic use cases.

  • 3,830
Loading comments...

More from Gerard Sans