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
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
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
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
Change Detection
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
- Data Services
- State Management
- ng2-redux (Redux)
- ngrx/store (RxJS 5)
- GraphQL + Apollo Client
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.
- 6,173