Reactive Angular2
October 25, 2016
Rob Wormald
@robwormald
Rob Wormald
Developer Advocate @ Google
Mildly Obsessed With Reactive Programming
Reactive APIs in Angular 2
A bit of history
BUT FETCH?
BUT SCARY
BUT SCARY
Promise
Promises
WHAT ARE YOU DOING NOOOO
Observables are about composition made easy
Reactive Forms - Typeahead
“Any time somebody talks about Observables and mentions type-aheads, take a drink”
Observable APIs in Angular 2
Reactive Forms - Form Group
<input type=”text” [formControl]=”myControl”>
<ul>
<li *ngFor=”r of results”>{{ r.name }}</li>
<ul>
Reactive Forms - Form Group
export class AppComponent {
myControl = new FormControl();
}
Reactive Forms - Value Changes
export class AppComponent {
myControl = new FormControl();
constructor(){
this.myControl.valueChanges
.subscribe(text => console.log(text));� }
}
Reactive Forms - Validity Changes
export class AppComponent {
myControl = new FormControl();
constructor(){
this.myControl.statusChanges
.subscribe(text => console.log(text));� }
}
Reactive Forms - Typeahead
export class AppComponent {
myControl = new FormControl();
constructor(){
this.myControl.valueChanges
.subscribe(text => console.log(text));� }
}
Reactive Forms - Typeahead
export class AppComponent {
myControl = new FormControl();
constructor(http:Http){
this.myControl.valueChanges
.subscribe(text => console.log(text));� }
}
Reactive Forms - Typeahead - map to URL
export class AppComponent {
myControl = new FormControl();
constructor(http:Http){
this.myControl.valueChanges
.map(text => `http://api.com?q=${text}`)
.subscribe(url => console.log(url));� }
}
Reactive Forms - Typeahead - flatMap Request => Response
export class AppComponent {
myControl = new FormControl();
constructor(http:Http){
this.myControl.valueChanges
.map(text => `http://api.com?q=${text}`)
.flatMap(url => http.get(url))
.subscribe(response => console.log(response));� }
}
Reactive Forms - Typeahead - flatMap Request => Response
export class AppComponent {
myControl = new FormControl();
constructor(http:Http){
this.myControl.valueChanges
.map(text => `http://api.com?q=${text}`)
.flatMap(url => http.get(url))
.map(res => res.json())
.subscribe(results => console.log(results));� }
}
Reactive Forms - Typeahead - Result Selector
export class AppComponent {
myControl = new FormControl();
constructor(http:Http){
this.myControl.valueChanges
.map(text => `http://api.com?q=${text}`)
.flatMap(url => http.get(url), (req, res) => res.json())
.subscribe(results => console.log(results));� }
}
Reactive Forms - Typeahead - In-flight requests
export class AppComponent {
myControl = new FormControl();
constructor(http:Http){
this.myControl.valueChanges
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())
.subscribe(results => console.log(results));� }
}
Reactive Forms - Typeahead - Async Pipe
export class AppComponent {
myControl = new FormControl();
results: any[];
constructor(http:Http){
this.myControl.valueChanges
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())
.subscribe(results => this.results = results);� }
}
Reactive Forms - Typeahead - Async Pipe
<input type=”text” [formControl]=”myControl”>
<ul>
<li *ngFor=”r of results”>{{ r.name }}</li>
<ul>
Reactive Forms - Typeahead - Async Pipe
export class AppComponent {
myControl = new FormControl();
results: Observable<any[]>
constructor(http:Http){
this.results = this.myControl.valueChanges
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())� }
}
Reactive Forms - Typeahead - Async Pipe
<input type=”text” [formControl]=”myControl”>
<ul>
<li *ngFor=”r of results | async”>{{ r.name }}</li>
<ul>
Reactive Forms - Typeahead - Optimizing
export class AppComponent {
myControl = new FormControl();
results: Observable<any[]>
constructor(http:Http){
this.results = this.myControl.valueChanges
.filter(text => text.length > 2)
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())� }
}
Reactive Forms - Typeahead - Optimizing
export class AppComponent {
myControl = new FormControl();
results: Observable<any[]>
constructor(http:Http){
this.results = this.myControl.valueChanges
.filter(text => text.length > 2)
.debounceTime(150)
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())� }
}
Reactive Forms - Typeahead - Reusable Logic with .let()
export class AppComponent {
myControl = new FormControl();
results: Observable<any[]>
constructor(http:Http){
this.results = this.myControl.valueChanges
.filter(text => text.length > 2)
.debounceTime(150)
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())� }
}
Reactive Forms - Typeahead - Reusable Logic with .let()
function filterAndDebounce(input:Observable<string>){
return input
.filter(text => text.length > 2)
.debounceTime(150)
}
Reactive Forms - Typeahead - Reusable Logic with .let()
export class AppComponent {
myControl = new FormControl();
results: Observable<any[]>
constructor(http:Http){
this.results = this.myControl.valueChanges
.let(filterAndDebounce)
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())� }
}
Reactive Forms - Typeahead - Composition
function filterAndDebounceFor(input:Observable<string>){
return (delay) => input
.filter(text => text.length > 2)
.debounceTime(delay)
}
Reactive Forms - Typeahead - Reusable Logic with .let()
export class AppComponent {
myControl = new FormControl();
results: Observable<any[]>
constructor(http:Http){
this.results = this.myControl.valueChanges
.let(filterAndDebounceFor(150))
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())� }
}
Reactive Forms - Typeahead - Reusable Logic with .let()
export class AppComponent {
myControl = new FormControl();
results: Observable<any[]>
constructor(http:Http){
this.results = this.myControl.valueChanges
.let(filterAndDebounceFor(150))
.map(text => `http://api.com?q=${text}`)
.switchMap(url => http.get(url), (req, res) => res.json())� }
}
Router Composition
Router Composition
Detail View
List View
Reactive Routing - List Component
export class ListComponent {
users : Observable<User[]>
constructor(userService:UserService){
this.users = userService.fetchUsers();� }
}
Reactive Routing - Detail Component
export class DetailComponent {
constructor(userService:UserService, route:ActivatedRoute){
� }
}
Reactive Routing - Detail Component
export class DetailComponent {
constructor(userService:UserService, route:ActivatedRoute){
route.params.subscribe(params => { … });� }
}
Reactive Routing - Detail Component
export class DetailComponent {
constructor(userService:UserService, router:Router){
router.queryParams.subscribe(params => { … });� }
}
Reactive Routing - Detail Component
export class DetailComponent {
constructor(userService:UserService, route:ActivatedRoute){
route.params
.flatMap(params => userService.getUserById(params.id))
.subscribe(userProfile => console.log(userProfile));
}
}
Reactive Routing - Detail Component
export class DetailComponent {
constructor(userService:UserService, route:ActivatedRoute){
route.params
.switchMap(params =>
userService.getUserById(params.id))
.subscribe(userProfile => console.log(userProfile));
}
}
Reactive Routing - Detail Component
export class DetailComponent {
user:Observable<User>
constructor(userService:UserService, route:ActivatedRoute){
this.user = route.params
.switchMap(params =>
userService.getUserById(params.id));
}
}
Reactive Routing - Detail Component View
<h1>User Profile</h1>
<div>
<span>{{ (user | async)?.name }}</span>
<span>{{ (user | async)?.id }}</span>
<img [src]=”(user | async)?.profile_url”></img>
</div>
Reactive Routing - Detail Container Component
export class DetailContainerComponent {
user:Observable<User>
constructor(userService:UserService, route:ActivatedRoute){
this.user = route.params
.switchMap(params =>
userService.getUserById(params.id));
}
}
Reactive Routing - Profile Component
<div>
<span>{{ user.name }}</span>
<span>{{ user.id }}</span>
<img [src]=”user.profile_url”></img>
<div>
export class ProfileComponent {
@Input() user:User = {}
}
Reactive Routing - Detail Container Component Refactored
<h1>User Profile</h1>
<user-profile [user]=”user | async”></user-profile>
Soon?
export class ProfileComponent {
@ObserveChild(SomeComponent, ‘click’) clicks;
@ObserveChildren(‘kids’, ‘customEvent’) customEvents;
}
The End!