Weitere ähnliche Inhalte
Ähnlich wie Angular mix chrisnoring (20)
Mehr von Christoffer Noring (20)
Kürzlich hochgeladen (20)
Angular mix chrisnoring
- 1. How SPAs got their groove back
Christoffer Noring
Google Developer Expert
@chris_noring
- 2. © AngularMIX All rights reserved.
https://www.angularmix.com
Speaker: Christoffer Noring
Google Developer Expert
Telerik Developer Expert ( NativeScript )
Fullstack Developer at Digital McKinsey
London Javascript meetup Organizer
Author of RxJS Ultimate
- 3. © AngularMIX All rights reserved.
https://www.angularmix.com
Introduction
Overview
The lost groove
Async and Callback Hell
The limitations of Promises
Introducing the App fu
RxJS beginner to master
Everything is not Response/ Request
Web Sockets, Socket.io
E.g Updated feeds and polling, Earthquake API, Firebase
All together now – The Feed pattern
We all need a Rob in a our lives
- 4. © AngularMIX All rights reserved.
https://www.angularmix.com
Why should I care?
Remember, we write code for humans
Knowing RxJS and how to use it Angular will:
Make you write less code
Make less mistakes that leads to memory leaks
Make you write simpler code that is easier to maintain
- 5. © AngularMIX All rights reserved.
https://www.angularmix.com
We’ve lost our direction ( groove )
Dealing with async code is painful
Our paradigm is usually how over what = we write too much code
- 6. © AngularMIX All rights reserved.
https://www.angularmix.com
Async history
callback hell
getData('url', data => {
getMoreData('url2/'+ data.id, (moreData) => {
getEvenMoreData('url3' + moreData.id, (evenMoreData) => {
/* do something*/
});
});
});
Pain, so much pain
- 7. © AngularMIX All rights reserved.
https://www.angularmix.com
Async history
enter Promises
getData()
.then( getMoreData )
.then( getEvenMoreData )
.catch( handleError );
Now we are talking, readable async
- 8. © AngularMIX All rights reserved.
https://www.angularmix.com
Async present day
async/await
async function get () {
let data = await getData('url');
let moreData = await getMoreData('url2' + data.id);
let evenMore = await getEvenMore('url3',moreData.id);
return evenMore;
}
get().then( data => console.log( 'data', data ));
Everything is great or?
- 9. © AngularMIX All rights reserved.
https://www.angularmix.com
Promises are limited
what to use for complex scenarios?
Returns one value
Hard to retry
No cancellation
Doesn’t mix easily with other async concepts
- 10. © AngularMIX All rights reserved.
https://www.angularmix.com
There is a better to way to do async
and its part of Angular – RxJS
from Daniel La Russo to Miyagi
- 11. © AngularMIX All rights reserved.
https://www.angularmix.com
1 2 3
Time
Observable – stream of data over time
- 12. © AngularMIX All rights reserved.
https://www.angularmix.com
Observables
let stream$ = Observable.create( observer => {
observer.next( 1 );
});
stream$.subscribe(
data => console.log('data', data)
);
Generate value here
Subscribe here
- 13. © AngularMIX All rights reserved.
https://www.angularmix.com
Observables + error
let stream$ = Observable.create( observer => {
observer.next( 1 );
observer.error('err');
});
stream$.subscribe(
data => console.log('data', data),
err => console.error('err', err) // err
);
Error produced
Subscribe to error here
- 14. © AngularMIX All rights reserved.
https://www.angularmix.com
Observables - completion
let stream$ = Observable.create( observer => {
observer.next( 1 );
observer.complete();
});
stream$.subscribe(
data => console.log('data', data),
err => console.error('err', err),
() => console.log('complete')
);
Complete the stream
Subscribe to complete
No more value will be generated
- 15. © AngularMIX All rights reserved.
https://www.angularmix.com
let stream$ = Observable.create( observer => {
let counter = 0;
let id = setInterval(() => observer.next(counter++), 1000);
return function(){ clearInterval(id); }
});
let subscription = stream$.subscribe(
data => console.log(‘data’, data)
);
setTimeout(() => subscription.unsubscribe());
Cancelling aka unsubscribing
Cleaning up resources
Define a clean up method
- 16. © AngularMIX All rights reserved.
https://www.angularmix.com
But mostly because Kobra Kai says we can’t
Knowledge is understanding how to build it,
So let’s build our own RxJS
- 17. © AngularMIX All rights reserved.
https://www.angularmix.com
create() operator
subscribe()
unsubscribe()
adding a filter() operator
What to build
- 18. © AngularMIX All rights reserved.
https://www.angularmix.com
We should have a behaviour function
that takes an observer as a parameter
We should have a create method
on our Observable class
We should have an Observable class
let stream$ = Observable.create( observer => {
observer.next( 1 );
});
Create a stream
- 19. © AngularMIX All rights reserved.
https://www.angularmix.com
create() needs to return an Observable instance and
save the behaviour function
class Observable {
constructor( behaviourFn ) { this.behaviourFn = behaviourFn; }
create( behaviourFn ) { return new Observable(behaviourFn); }
}
Observer class
- 20. © AngularMIX All rights reserved.
https://www.angularmix.com
Data callback
We need to define an Observer class
class Observable {
constructor( behaviourFn ) { this.behaviourFn = behaviourFn; }
create(behaviourFn) { return new Observable(behaviourFn); }
subscribe( dataFn ) {
behaviourFn( observer );
}
}
stream$.subscribe( data => console.log(data));
And this guy, dataFn
needs to go somewhere
Subscribe to the stream
- 21. © AngularMIX All rights reserved.
https://www.angularmix.com
class Observer {
constructor( dataFn ) { this.dataFn = dataFn; }
next(val) { this.dataFn( val ); }
}
When we call next(),
we need to call dataFn()
Observer class
- 22. © AngularMIX All rights reserved.
https://www.angularmix.com
class Observable {
constructor( behaviourFn ) { this.behaviourFn = behaviourFn; }
create(behaviourFn) { return new Observable(behaviourFn); }
subscribe( dataFn ) {
let observer = new Observer( dataFn );
behaviourFn( observer );
}
}
provide dataFn() to Observer so it may be used when calling
.next()
Update Observable class
- 23. © AngularMIX All rights reserved.
https://www.angularmix.com
let stream$ = Observable.create( observer => {
observer.next(1);
observer.next(2);
});
stream$.subscribe( data => console.log(‘sub1’, data));
// Sub1 1, Sub1 2
stream$.subscribe( data => console.log(‘sub2’, data));
// Sub2 1,Sub 2 2
It works!!
Put it to the test
- 24. © AngularMIX All rights reserved.
https://www.angularmix.com
We are one with the stream
- 25. © AngularMIX All rights reserved.
https://www.angularmix.com
let stream$ = Observable.create( observer => {
let counter = 0;
let id = setInterval(() => {
observer.next(counter++);
}, 1000);
return function() { clearInterval(id); }
});
Let’s create something
worth unsubscribing to
Let’s define a clean up
method
Add unsubscribe
- 26. © AngularMIX All rights reserved.
https://www.angularmix.com
class Observable {
constructor( behaviourFn ) { this.behaviourFn = behaviourFn; }
create(behaviourFn) { return new Observable(behaviourFn); }
subscribe( dataFn ) {
let observer = new Observer( dataFn );
let cleanUpFn = behaviorFn( observer );
return {
unsubscribe : cleanUpFn
};
}
}
behaviourFn IS our clean up function,
just return it from the unsubscribe() method
Let’s update Observable class
- 27. © AngularMIX All rights reserved.
https://www.angularmix.com
let stream$ = Observable.create( observer => {
let counter = 0;
let id = setInterval(() => {
observer.next(counter++);
}, 1000);
return function() { clearInterval(id); }
});
stream$.unsubscribe();
This is called
When we call this
And using it
- 28. © AngularMIX All rights reserved.
https://www.angularmix.com
Cleanup successful
- 29. © AngularMIX All rights reserved.
https://www.angularmix.com
Summary so far
Observables
Observer
Unsubscribe
We know how to build
- 30. © AngularMIX All rights reserved.
https://www.angularmix.com
Wisdom is to NOT build it, because other people do
it better
And there is a GitHub project already,
so contribute
- 31. © AngularMIX All rights reserved.
https://www.angularmix.com
Operators
The thing that empowers Observables
- 32. © AngularMIX All rights reserved.
https://www.angularmix.com
Operators
Operators can:
- Operate on your data
- Decide the speed of the stream
- Combine streams
- Change streams
And much more
let stream$ = Rx.Observable
.of(1, 2, 3);
.operator()
.operator()
.operator();
Define your stream
Apply operator after operator
- 33. © AngularMIX All rights reserved.
https://www.angularmix.com
Operators- categories
Construction Conversion Combination
Mathematical Time Grouping
- 34. © AngularMIX All rights reserved.
https://www.angularmix.com
There is an operator for everything
let stream$ = Rx.Observable
.of(1,2,3)
.map( )
.filter()
.toPromise()
.then(
data => console.log(data)
)
Don’t make bunny sad though
Even for converting Observable to
Promise
- 35. © AngularMIX All rights reserved.
https://www.angularmix.com
Operators- build it yourself
- 36. © AngularMIX All rights reserved.
https://www.angularmix.com
class Observable {
constructor( behaviour ) {
this.behaviour = behaviour;
}
filter(fnFilter) {
let obs = new FilterableObservable(this.behaviour, fnFilter);
return obs;
}
static create( behaviour ) {
return new Observable( behaviour );
}
subscribe( fnData ) {
let observer = new Observer(fnData);
this.behaviour( observer );
}
}
Add filter() – update our Observable class
We need a new
type of Observable
We send in behaviour
and filter function
- 37. © AngularMIX All rights reserved.
https://www.angularmix.com
class FilterableObservable extends Observable {
constructor(behaviour, filter) {
super(behaviour);
this.filter = filter;
}
subscribe(fnData) {
let observer = new Observer( fnData );
observer.next = (val) => {
if(this.filter(val)){ fnData( val ); }
};
this.behaviour( observer );
}
}
FilterableObservable
For every value, check
with our filter() if it should
be emitted
We even know how to build
Operators now
- 38. © AngularMIX All rights reserved.
https://www.angularmix.com
Before talking about Angular with RxJS
Wrap Observable
Change from
one stream to the next
Let’s learn a couple of more concepts
- 39. © AngularMIX All rights reserved.
https://www.angularmix.com
let stream$ = Rx.Observable.create(observer => {
someAsyncFunction((val) => {
observer.next( val );
observer.complete();
});
});
stream$.subscribe( data => {
console.log('async data', data);
});
Wrapping, fulfill the contract
function someAsyncFunction(cb){
setTimeout(() => { cb('some value'); },3000);
}
Allows us to make anything async
part of Observables and RxJS
A callback function turned into an Observable
- 40. © AngularMIX All rights reserved.
https://www.angularmix.com
let elem = document.getElementById('text');
let stream = Rx.Observable
.fromEvent(elem,'keyup')
.switchMap( data => {
return new Rx.Observable.of('some data back based on: '+ data);
});
stream.subscribe( data => {
console.log('data', data);
})
Change from one type of stream to the next
flatMap(), switchMap()
keyUp stream
Change into new stream
- 41. © AngularMIX All rights reserved.
https://www.angularmix.com
Working with switchMap/flatMap is a key concept
You are able to divide up your code into multiple stream,
Aka you are able to break down the problem
keykey key
AJAX AJAX AJAX
Combine different types of streams
to build your algorithm
It will also make it easier to isolate errors into
a specific stream
- 42. © AngularMIX All rights reserved.
https://www.angularmix.com
Angular comes with RxJS in everything
- 43. © AngularMIX All rights reserved.
https://www.angularmix.com
Angular comes with RxJS
an async scenario that grows with you
class Service{
constructor(private http:HttpClient) {}
getData() {
return this.httpClient
.get('url')
.map( this.mapPeople );
}
mapPeople():Array<Person> {
return json => json.map( person => new Person(person));
}
}
Get your HTTP data with HttpClien
- 44. © AngularMIX All rights reserved.
https://www.angularmix.com
export class PeopleComponent
implements OnInit,
implements OnDestroy {
people:Array<Person>;
subscription;
constructor(private srv: Service) {}
ngOnInit() {
this.subscription = srv.getData()
.subscribe( data => this.people = data );
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Subscribe
Call unsubscribe on destroying
component
Angular WITHOUT async pipe
- 45. © AngularMIX All rights reserved.
https://www.angularmix.com
Angular removes Cancelling
with async pipe@Component({
template : `
<div *ngFor="let person of people$ | async">
{{ person.name }}
</div>
`
})
export class PeopleComponent implements OnInit {
people$:Observable<Person>;
constructor(private srv: Service) {}
ngOnInit() { this.people$ = srv.getData(); }
}
No more
subscribe()
Or
Unsubscribe()
- 46. © AngularMIX All rights reserved.
https://www.angularmix.com
class Service{
people: Array<Person>;
constructor(private http:HttpClient) {}
getData() {
return this.httpClient
.get('url')
.map( this.mapPeople )
.do( data => localStorage.setItem(‘people’,data); )
.catch( err => {
let people = localStorage.getItem(‘people’);
return of(people !== null ? people : []);
})
}
}
Handle offline
with localStorage
Save response to cac
Retrieve cached response
if erroring out
- 47. © AngularMIX All rights reserved.
https://www.angularmix.com
class Service{
constructor(private http:HttpClient) {}
getData() {
return this.httpClient
.get('url')
.map( this.mapPeople )
.retryWhen( err => err.delay(200) )
.take(noOfAttemptsBeforeGivingUp)
}
}
Handle shaky connections
with retryWhen
Delays each attempt with 200ms,
Also retries x times
Ok to do, but stream finishes,
after x attempts
- 48. © AngularMIX All rights reserved.
https://www.angularmix.com
class Service{
constructor(private http:HttpClient) {}
getData() {
return this.httpClient
.get('url')
.map( this.mapPeople )
.retryWhen( err => {
err
.scan((errCount, err) =>
if(errCount === 2) { throw ‘giving up’ }
else { return errCount + 1; }
,0)
.delay(200 * errCount) // progressive increase
time
})
.take(noOfAttemptsBeforeGivingUp) // 404,500
}
}
Handle shaky connections
better solution
stream$.subscribe(
data => console.log(
'err stream',
data
),
err => console.log(
'erroring out after x retries',
respondWithCachedData)
)
errorCount, delay and termination
Sort out why it failed to see
wether it is worth retrying
- 49. © AngularMIX All rights reserved.
https://www.angularmix.com
From a service to a feed
What is a feed?
Why do we want a feed?
There might be more than one component wanting to consume the data
We don’t want to do unnecessary HTTP calls
An exposed Observable that can have many subscribers
- 50. © AngularMIX All rights reserved.
https://www.angularmix.com
From a service to a feed
class Service{
people: Array<Person>;
constructor(private http:HttpClient) {
this.fetchData();
}
fetchData() {
return this.httpClient
.get('url')
.map( this.mapPeople )
.subscribe( data => this.people = data );
}
} @Component({})
export class PeopleComponent implements OnInit
{
private people: Array<Person>;
constructor(private srv: PersonService) { }
get people() { return this.srv.people; }
}
We can do this with a minimum of RxJS,
Trust in Angulars change detection
Use this for Change Detection
to trigger
- 51. © AngularMIX All rights reserved.
https://www.angularmix.com
What did we learn on services?
We should handle offline
We should handle shaky connections
How to build a feed service
- 52. © AngularMIX All rights reserved.
https://www.angularmix.com
There is more than HTTP
HTTP
Web Sockets, Full Duplex connections
Firebase
Socket.io
HTTP + Polling feeds
And it affects how we write our
service and consumes it
- 53. © AngularMIX All rights reserved.
https://www.angularmix.com
Building our service
add Socket.io
import * as io from 'socket.io-client';
export class TaskService {
subscription;
tasks:Task[] = [];
constructor(private http:Http) {
this.fetchData();
this.socket = io(this.url);
this.socket.on('task', (data) => {
this.tasks = [ ...this.tasks, data ];
});
}
private fetchData() { /* omitted */ }
}
We can use our feed pattern,
but what if we want to
listen to changes?
And display CSS
- 54. © AngularMIX All rights reserved.
https://www.angularmix.com
Two different approaches
we want to know when a change happens
One of the few cases when a Subject might be need
.share()
.replay(1)
BehaviourSubject
- 55. © AngularMIX All rights reserved.
https://www.angularmix.com
Adding BehaviourSubject
export class TaskService {
private internalStore:BehaviourSubject;
constructor() { this.internalStore = new BehaviourSubject([]); }
get store() { return this.internalStore.asObservable(); }
private fetchTasks(){
this.http
.get('/data/tasks.json')
.map(response => response.json())
.map(this.mapTasks)
.do( data => {
this._store.next( data );
localStorage.setItem('tasks', JSON.stringify(data))
})
.catch( err => {
return Rx.Observable.of(this.fetchLocalStorage());
});
}
}
Initialize Subject
Expose as Observable, aka
defensive coding
Gives us start data,
also remembers last emitted
- 56. © AngularMIX All rights reserved.
https://www.angularmix.com
Building our service
consuming our service
@Component({})
export class TaskComponent implements OnInit {
constructor(private srv: TaskService) {}
ngOnInit() {
this.srv.store.subscribe( tasks => {
// tasks where updated somehow, show CSS
});
}
}
Only when we care about changes,
REMEMBER most of the time we don’t need a Subject
- 57. © AngularMIX All rights reserved.
https://www.angularmix.com
Earthquake API
HTTP Polling
The title (28 pt) uses the dark red (the 4th color in the template)
The subtitle is 1 size smaller (24 pt) and should always uses the theme
gray color (2nd color in the template)
Text here is 20 pt
18 pt
16 pt
- 58. © AngularMIX All rights reserved.
https://www.angularmix.com
Mixing different async concepts
Rich composition
Naive Little better Angular version
- 59. © AngularMIX All rights reserved.
https://www.angularmix.com
First naive approach
let elem = document.getElementById('text');
let stream = Rx.Observable
.fromEvent(elem,'keyup')
.map( ev => ev.key )
.filter( key => {
return elem.value.length > 3;
})
.switchMap( data => {
return new Rx.Observable.of('some data back based on: '+ data);
});
Only do something when
We have enough entered charac
From keystream to AJAX stream, playing nice with
other async concepts – rich composition
- 60. © AngularMIX All rights reserved.
https://www.angularmix.com
Better approach
let elem = document.getElementById('text');
let stream = Rx.Observable
.fromEvent(elem,'keyup')
.map( ev => ev.key )
.debounceTime(500)
.switchMap( data => {
return new Rx.Observable.of('some data back
based on: '+ data);
});
Wait til the user stopped
typing for 500ms
- 61. © AngularMIX All rights reserved.
https://www.angularmix.com
Angular approach - service
export class ServiceService {
constructor(private http: HttpClient) {}
httpSearch(inputText: string) {
return this.http.get('url?query=${inputText}');
}
}
A simple http.get() call
- 62. © AngularMIX All rights reserved.
https://www.angularmix.com
Angular approach - component
<input type="text" [formControl] = "input" class="form-control” />
<div *ngFor="let term in result | async">
{{term}}
</div>
@Component({})
export class SearchComponent {
result$:Observable;
input = new FormControl();
constructor(private srv:SearchService) {
this.result$ = this.input
.valueChanges
.debounceTime(500)
.distinctUntilChanged()
.switchMap(inputText => this.srv.httpSearch(inputText))
}
}
Capture change,
when it happens
- 63. © AngularMIX All rights reserved.
https://www.angularmix.com
Summary
We actually understand how to build RxJS
Angular helps out with async pipe and its own change detection
RxJS does a lot of the heavy lifting thanks to
Operators for manipulating data
Operators for advanced error scenarios such as retry etc..
Some constructs in RxJS should be sparsely used like Subject
We can rich composition, cause everything async can be made into
an Observable
- 64. © AngularMIX All rights reserved.
https://www.angularmix.com
My free gitbook on RxJS
angular.io/resources RxJS 5 Ultimate
Help me make it better or just tell your friends
Power to the community
- 65. © AngularMIX All rights reserved.
https://www.angularmix.com
Thank you
promise
.retry(4)
.kungfuKid(1)
@chris_noring