SlideShare ist ein Scribd-Unternehmen logo
1 von 57
Downloaden Sie, um offline zu lesen
Angular and The
Case for RxJS
Sandi K. Barr
Senior Software Engineer
Multiple values over time
Cancellable with unsubscribe
Synchronous or asynchronous
Declarative: what, not how or when
Nothing happens without an observer
Separate chaining and subscription
Can be reused or retried
(not a spicy Promise)Observable
!! ! !!
Observable(lazy)
Promise(eager)
Create: new Observable((observer) => observer.next(123)); new Promise((resolve, reject) => resolve(123));
Transform: obs$.pipe(map((value) => value * 2)); promise.then((value) => value * 2);
Subscribe: sub = obs$.subscribe((value) => console.log(value)); promise.then((value) => console.log(value));
Unsubscribe: sub.unsubscribe(); // implied by promise resolution
https://angular.io/guide/comparing-observables#cheat-sheet
Observable
Event
button.addEventListener('click', e => console.log('Clicked', e));
button.removeEventListener('click');
https://angular.io/guide/comparing-observables#observables-compared-to-events-api
const clicks$ = fromEvent(button, 'click');
const subscription = clicks$.subscribe(event => console.log('Clicked', event));
subscription.unsubscribe();
Observable
a function that takes
an Observer
const subscription = observable.subscribe(observer);
subscription.unsubscribe();
Observer
an object with next, error,
and complete methods
const observer = {
next: value => console.log('Next value:', value),
error: err => console.error('Error:', err),
complete: () => console.log('Complete')
};
const subscription = observable.subscribe(observer);
Subscription
an Observable only produces
values on subscribe()
A long time ago,
We used to be friends,
But I haven’t thought of
you lately at all...
Termination is not Guaranteed
https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339
Cold ObsERvables
The producer is created during the subscription
https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339
…
HOT ObsERvables
The producer is created outside the subscription
https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339
…
SubjecT: both an
observable and an observer
Make a cold Observable hot
https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339
…
Subject has state
Keeps a list of observers and sometimes also a number of the values that have been emitted
RxJS
Steep Learning Curve
@Injectable()
export class TodoStoreService {
private _todos: BehaviorSubject<Todo[]> = new BehaviorSubject([]);
constructor(private todoHTTP: TodoHttpService) {
this.loadData();
}
get todos(): Observable<Todo[]> {
return this._todos.asObservable();
}
loadData() {
this.todoAPI.getTodos()
.subscribe(
todos => this._todos.next(todos),
err => console.log('Error retrieving Todos’)
);
}
} https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts
Store: Observable Data Service
Provide data to multiple parts of an application
BehaviorSubject
Just because you can access the value directly...
https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-behavior-subject-getvalue-ts
const subject = new BehaviorSubject(initialValue);
// some time later…
const value = subject.getValue();
@Injectable()
export class TodoStoreService {
private _todo: BehaviorSubject<Todo[]> = new BehaviorSubject([]);
constructor(private todoAPI: TodoHttpService) {
this.loadData();
}
get todos(): Observable<Todo[]> {
return this._todos.asObservable();
}
}
https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts
Protect the BehaviorSubject with
asObservable()
Components can subscribe after the data arrives
@Component({
selector: 'app-todo-list',
template: `
<ul>
<app-todo-list-item
*ngFor="let todo of todos"
[todo]="todo">
</app-todo-list-item>
</ul>
`
})
export class TodoListComponent implements OnInit, OnDestroy {
todos: Todo[];
todosSub: Subscription;
constructor (private todoStore: TodoStoreService) {}
ngOnInit() {
this.todosSub = this.todoStore.todos.subscribe(todos => {
this.todos = todos;
});
}
ngOnDestroy() {
this.todosSub.unsubscribe();
}
}
LET ME
HAVE THAT
TODO List
CLEAN UP
SUBSCRIPTIONS
@Component({
selector: 'app-todo-list',
template: `
<ul>
<app-todo-list-item
*ngFor="let todo of todos$ | async"
[todo]="todo">
</app-todo-list-item>
</ul>
`
})
export class TodoListComponent {
todos$: Observable<Todo[]> = this.todoStore.todos;
constructor (private todoStore: TodoStoreService) {}
}
USE THE ASYNC PIPE TO
AVOID MEMORY LEAKS
Subscriptions live until a stream is completed or until they are manually unsubscribed
https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/components/todo-list.component.ts
Decouple responsibilities
Observable Data Service
Provide data to multiple parts of an application
Not the same as centralized state management
Observable
Data SERVICE
HTTP SERVICE
COMPONENTS
!=
Action method updates the store on success
Store: Observable Data Service
https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts
addTodo(newTodo: Todo): Observable<Todo> {
const observable = this.todoAPI.saveTodo(newTodo);
observable.pipe(
withLatestFrom(this.todos),
).subscribe(( [savedTodo, todos] ) => {
this._todos.next(todos.concat(savedTodo));
});
return observable;
}
Avoid duplicating HTTP requests!
https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts
HTTP Observables are Cold
addTodo(newTodo: Todo): Observable<Todo> {
const observable = this.todoAPI.saveTodo(newTodo);
observable.pipe(
withLatestFrom(this.todos),
).subscribe(( [savedTodo, todos] ) => {
this._todos.next(todos.concat(savedTodo));
});
return observable;
}
@Injectable()
export class TodoHttpService {
base = 'http://localhost:3000/todos';
constructor(private http: HttpClient) { }
getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(this.base);
}
saveTodo(newTodo: Todo): Observable<Todo> {
return this.http.post<Todo>(this.base, newTodo, {headers}).pipe(share());
}
}
share() makes a cold Observable hot
HTTP Service
https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/http/todo-http.service.ts
OPERATORS
Compose complex
asynchronous code in a
declarative manner
Pipeable Operators: take an input Observable and generate a resulting output Observable
Examples: filter, map, mergeMap
Creation Operators: standalone functions to create a new Observable
Examples: interval, of, fromEvent, concat
share() : refCount
share() also makes Observables retry-able
https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/http/todo-http.service.ts
@Injectable()
export class TodoHttpService {
base = 'http://localhost:3000/todos';
constructor(private http: HttpClient) { }
getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(this.base);
}
saveTodo(newTodo: Todo): Observable<Todo> {
return this.http.post<Todo>(this.base, newTodo, {headers}).pipe(share());
}
}
" HOT: Share the execution
⛄ COLD: Invoke the execution
async : Unsubscribes automatically
https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-duplicate-http-component-ts
But still be sure to avoid duplicating HTTP requests!
@Component({
selector: 'app-todo',
template: `
Total #: {{ total$ | async }}
<app-todo-list [todos]="todos$ | async"></app-todo-list>
`
})
export class DuplicateHttpTodoComponent {
todos$ = this.http.get<Todo[]>('http://localhost:3000/todos');
total$ = this.todos$.pipe(map(todos => todos.length));
constructor(private http: HttpClient) {}
}
ngIf with | async as
https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-async-as-component-ts
Assign Observable values to a local variable
@Component({
selector: 'app-todo',
template: `
<ng-container *ngIf="todos$ | async as todos">
Total #: {{ todos.length }}
<app-todo-list [todos]="todos"></app-todo-list>
</ng-container>
`
})
export class TodoComponent {
todos$ = this.http.get<Todo[]>('http://localhost:3000/todos');
constructor(private http: HttpClient) {}
}
ngIf with| async as with ;ngElse
https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-async-as-else-component-ts
Show alternate block when Observable has no value
@Component({
selector: 'app-todo',
template: `
<ng-container *ngIf="todos$ | async as todos; else loading">
Total #: {{ todos.length }}
<app-todo-list [todos]="todos"></app-todo-list>
</ng-container>
<ng-template #loading><p>Loading...</p></ng-template>
`
})
export class TodoComponent {
todos$ = this.http.get<Todo[]>('http://localhost:3000/todos');
constructor(private http: HttpClient) {}
}
Reactive Templates$ $
Compose a series of operators
Observable.prototype.pipe()
import { map, filter, scan } from 'rxjs/operators';
import { range } from 'rxjs';
const source$ = range(0, 10);
source$.pipe(
filter(x => x % 2 === 0),
map(x => x + x),
scan((acc, x) => acc + x, 0)
).subscribe(x => console.log(x));
https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-pipe-ts
https://rxjs-dev.firebaseapp.com/assets/images/guide/marble-diagram-anatomy.svg
map()
A transformational operator
Applies a projection to each value
filter()
ONE OF MANY FILTERING operatorS
Filters items emitted from source
find()
A CONDITIONAL operator
Finds the first match then completes
reduce()
AN Aggregate operator
Emits aggregate result on completion
Emits accumulated result at each interval
scan()
A TRANSFORMATIONAL operator
@Component({
selector: 'app-todo-search',
template: `
<label for="search">Search: </label>
<input id="search" (keyup)="onSearch($event.target.value)"/>
`
})
export class TodoSearchComponent implements OnInit, OnDestroy {
@Output() search = new EventEmitter<string>();
changeSub: Subscription;
searchStream = new Subject<string>();
ngOnInit() {
this.changeSub = this.searchStream.pipe(
filter(searchText => searchText.length > 2), // min length
debounceTime(300), // wait for break in keystrokes
distinctUntilChanged() // only if value changes
).subscribe(searchText => this.search.emit(searchText));
}
ngOnDestroy() {
if (this.changeSub) { this.changeSub.unsubscribe(); }
}
onSearch(searchText: string) {
this.searchStream.next(searchText);
}
} https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/components/todo-search.component.ts
TYPE
AHEAD
SEARCH:
Rate-limit the input and delay the output
debounceTime()
A FILTERING operator
Emits values that are distinct from the previous value
distinctUntilChanged()
A FILTERING operator
AVOID NESTED SUBSCRIPTIONS
pyramid shaped callback hell% %
export class TodoEditComponent implements OnInit {
todo: Todo;
constructor(private todoStore: TodoStoreService,
private route: ActivatedRoute,
private router: Router) {}
ngOnInit() {
this.route.params.subscribe(params => {
const id = +params['id'];
this.todoStore.todos.subscribe((todos: Todo[]) => {
this.todo = todos.find(todo => todo.id === id);
});
});
}
}
Higher-Order Observables
Observables that emit other Observables
https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/components/edit/todo-edit.component.ts
export class TodoEditComponent {
todo$: Observable<Todo> = this.route.params.pipe(
map(params => +params['id']),
switchMap(id => this.todoStore.getTodoById(id))
);
constructor(private todoStore: TodoStoreService,
private route: ActivatedRoute,
private router: Router) {}
}
Cancels inner Observables
switch
Waits for inner Observables to complete
CONCAT
Subscribe to multiple inner Observables at a time
MERGE
// using map with nested subscribe
from([1, 2, 3, 4]).pipe(
map(param => getData(param))
).subscribe(val => val.subscribe(data => console.log(data)));
// using map and mergeAll
from([1, 2, 3, 4]).pipe(
map(param => getData(param)),
mergeAll()
).subscribe(val => console.log(val));
// using mergeMap
from([1, 2, 3, 4]).pipe(
mergeMap(param => getData(param))
).subscribe(val => console.log(val));
mergeMap()
Higher-order transformation operator
Luuk Gruijs: https://medium.com/@luukgruijs/understanding-rxjs-map-mergemap-switchmap-and-concatmap-833fc1fb09ff
Projects each source value into an Observable that is merged into the output Observable
Also provide the latest value from another Observable
withLatestFrom()
combineLatest()
Updates from the latest values of each input Observable
https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-combinelatest-withlatestfrom-ts
combineLatest( [notifications$, otherPrimaryActiities$] )
.subscribe({
next: ( [notification, otherPrimaryActivity] ) => {
// fires whenever notifications$ _or_ otherPrimaryActivities$ updates
// but not until all sources have emitted at least one value
}
});
notifications$.pipe( withLatestFrom(mostRecentUpdates$) )
.subscribe({
next: ( [notification, mostRecentUpdate] ) => {
// fires only when notifications$ updates and includes latest from mostRecentUpdates$
}
});
@Component({
selector: 'app-many-subscriptions',
template: `<p>value 1: {{value1}}</p>
<p>value 2: {{value2}}</p>
<p>value 3: {{value3}}</p>`
})
export class SubscriberComponent implements OnInit, OnDestroy {
value1: number;
value2: number;
value3: number;
destroySubject$: Subject<void> = new Subject();
constructor(private service: MyService) {}
ngOnInit() {
this.service.value1.pipe(
takeUntil(this.destroySubject$)
).subscribe(value => {
this.value1 = value;
});
this.service.value2.pipe(
takeUntil(this.destroySubject$)
).subscribe(value => {
this.value2 = value;
});
this.service.value3.pipe(
takeUntil(this.destroySubject$)
).subscribe(value => {
this.value3 = value;
});
}
ngOnDestroy() {
this.destroySubject$.next();
}
}
Always
Bring
Backup
with this
one weird trick
USE A SUBJECT
TO Complete
STREAMS
https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-weird-trick-takeuntil-ts-L1
tap()
a UTILITY operator
Perform side effects and return the stream unchanged
catchError()
an error handling operator
Catch errors in the stream and return a new Observable
catchError()
An error can terminate a stream and send the error to the error() callback,
or the stream can be allowed to continue if piped through catchError().
https://rxjs-dev.firebaseapp.com/guide/operators
Creation
fromEvent
interval
of
Aggregate
reduce
count
Conditional
find
every
isEmpty
Utility
tap
delay
toArray
Error Handling
catchError
retry
JOIN
mergeAll
withLatestFrom
Filtering
filter
debounceTime
distinctUntilChanged
Transformation
map
mergeMap
scan
Join Creation
merge
concat
combineLatest
Multicasting
share
publish
publishReplay
Aggregate
reduce
count
Conditional
find
every
isEmpty
Utility
tap
delay
toArray
Error Handling
catchError
retry
JOIN
mergeAll
withLatestFrom
Filtering
filter
debounceTime
distinctUntilChanged
Join Creation
merge
concat
combineLatest
Transformation
map
mergeMap
scan Creation
fromEvent
interval
of
Multicasting
share
publish
publishReplay
https://rxjs-dev.firebaseapp.com/guide/operators
Custom Functions to Add and Remove Event Handlers
generate
interval
fromEventPattern
fromEvent
Create a New
Observable
Sequence
That Works Like a for-Loop
That Never Does Anything
That Repeats a Value
That Throws an Error
That Completes
From an Event
From a Promise
That Iterates
That Emits Values on a Timer
Decided at Subscribe Time
Based on a Boolean Condition
Over Values in a Numeric Range
Over Values in an Iterable or Array-like Sequence
Over Arguments
With an Optional Delay
Using Custom Logic
Of Object Key/Values
repeat
throwError
EMPTY
NEVER
from
pairs
range
of
timer
iif
defer
usingThat Depends on a Resource
Troubleshooting
Has this Observable been
subscribed to?

How many Subscriptions
does this Observable have?

When does this Observable
complete? Does it complete?

Do I need to unsubscribe
from this Observable?
THANKS!
example code: https://github.com/sandikbarr/rxjs-todo
slides: https://www.slideshare.net/secret/FL6NONZJ7DDAkf

Weitere ähnliche Inhalte

Was ist angesagt?

RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)Tracy Lee
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux IntroductionNikolaus Graf
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooksSamundra khatri
 
React Router: React Meetup XXL
React Router: React Meetup XXLReact Router: React Meetup XXL
React Router: React Meetup XXLRob Gietema
 
Spring Boot and REST API
Spring Boot and REST APISpring Boot and REST API
Spring Boot and REST API07.pallav
 
RxJS & Angular Reactive Forms @ Codemotion 2019
RxJS & Angular Reactive Forms @ Codemotion 2019RxJS & Angular Reactive Forms @ Codemotion 2019
RxJS & Angular Reactive Forms @ Codemotion 2019Fabio Biondi
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentationritika1
 
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...Edureka!
 
Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Aaron Gustafson
 
Angular Lazy Loading and Resolve (Route Resolver)
Angular Lazy Loading and Resolve (Route Resolver)Angular Lazy Loading and Resolve (Route Resolver)
Angular Lazy Loading and Resolve (Route Resolver)Squash Apps Pvt Ltd
 
Angular - Chapter 4 - Data and Event Handling
 Angular - Chapter 4 - Data and Event Handling Angular - Chapter 4 - Data and Event Handling
Angular - Chapter 4 - Data and Event HandlingWebStackAcademy
 
Getting Started with NgRx (Redux) Angular
Getting Started with NgRx (Redux) AngularGetting Started with NgRx (Redux) Angular
Getting Started with NgRx (Redux) AngularGustavo Costa
 
How to implement internationalization (i18n) in angular application(multiple ...
How to implement internationalization (i18n) in angular application(multiple ...How to implement internationalization (i18n) in angular application(multiple ...
How to implement internationalization (i18n) in angular application(multiple ...Katy Slemon
 

Was ist angesagt? (20)

RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)
 
Angular 2 observables
Angular 2 observablesAngular 2 observables
Angular 2 observables
 
Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux Introduction
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooks
 
React Router: React Meetup XXL
React Router: React Meetup XXLReact Router: React Meetup XXL
React Router: React Meetup XXL
 
Spring Boot and REST API
Spring Boot and REST APISpring Boot and REST API
Spring Boot and REST API
 
Ngrx slides
Ngrx slidesNgrx slides
Ngrx slides
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
RxJS & Angular Reactive Forms @ Codemotion 2019
RxJS & Angular Reactive Forms @ Codemotion 2019RxJS & Angular Reactive Forms @ Codemotion 2019
RxJS & Angular Reactive Forms @ Codemotion 2019
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentation
 
React js
React jsReact js
React js
 
React hooks
React hooksReact hooks
React hooks
 
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
 
Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]
 
Angular Lazy Loading and Resolve (Route Resolver)
Angular Lazy Loading and Resolve (Route Resolver)Angular Lazy Loading and Resolve (Route Resolver)
Angular Lazy Loading and Resolve (Route Resolver)
 
Angular - Chapter 4 - Data and Event Handling
 Angular - Chapter 4 - Data and Event Handling Angular - Chapter 4 - Data and Event Handling
Angular - Chapter 4 - Data and Event Handling
 
Getting Started with NgRx (Redux) Angular
Getting Started with NgRx (Redux) AngularGetting Started with NgRx (Redux) Angular
Getting Started with NgRx (Redux) Angular
 
Angular Directives
Angular DirectivesAngular Directives
Angular Directives
 
How to implement internationalization (i18n) in angular application(multiple ...
How to implement internationalization (i18n) in angular application(multiple ...How to implement internationalization (i18n) in angular application(multiple ...
How to implement internationalization (i18n) in angular application(multiple ...
 

Ähnlich wie Angular and The Case for RxJS

Understanding reactive programming with microsoft reactive extensions
Understanding reactive programming  with microsoft reactive extensionsUnderstanding reactive programming  with microsoft reactive extensions
Understanding reactive programming with microsoft reactive extensionsOleksandr Zhevzhyk
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020Matt Raible
 
Flask patterns
Flask patternsFlask patterns
Flask patternsit-people
 
An intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECSAn intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECSYevgeniy Brikman
 
Flask and Angular: An approach to build robust platforms
Flask and Angular:  An approach to build robust platformsFlask and Angular:  An approach to build robust platforms
Flask and Angular: An approach to build robust platformsAyush Sharma
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 
リローダブルClojureアプリケーション
リローダブルClojureアプリケーションリローダブルClojureアプリケーション
リローダブルClojureアプリケーションKenji Nakamura
 
Writing Redis in Python with asyncio
Writing Redis in Python with asyncioWriting Redis in Python with asyncio
Writing Redis in Python with asyncioJames Saryerwinnie
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuningAOE
 
Building a js widget
Building a js widgetBuilding a js widget
Building a js widgetTudor Barbu
 
ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015Michiel Borkent
 

Ähnlich wie Angular and The Case for RxJS (20)

Understanding reactive programming with microsoft reactive extensions
Understanding reactive programming  with microsoft reactive extensionsUnderstanding reactive programming  with microsoft reactive extensions
Understanding reactive programming with microsoft reactive extensions
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
 
Play!ng with scala
Play!ng with scalaPlay!ng with scala
Play!ng with scala
 
Advanced redux
Advanced reduxAdvanced redux
Advanced redux
 
Arquitecturas de microservicios - Medianet Software
Arquitecturas de microservicios   -  Medianet SoftwareArquitecturas de microservicios   -  Medianet Software
Arquitecturas de microservicios - Medianet Software
 
angular fundamentals.pdf
angular fundamentals.pdfangular fundamentals.pdf
angular fundamentals.pdf
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
 
An intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECSAn intro to Docker, Terraform, and Amazon ECS
An intro to Docker, Terraform, and Amazon ECS
 
Flask and Angular: An approach to build robust platforms
Flask and Angular:  An approach to build robust platformsFlask and Angular:  An approach to build robust platforms
Flask and Angular: An approach to build robust platforms
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
Side effects-con-redux
Side effects-con-reduxSide effects-con-redux
Side effects-con-redux
 
リローダブルClojureアプリケーション
リローダブルClojureアプリケーションリローダブルClojureアプリケーション
リローダブルClojureアプリケーション
 
Server Side Swift: Vapor
Server Side Swift: VaporServer Side Swift: Vapor
Server Side Swift: Vapor
 
Writing Redis in Python with asyncio
Writing Redis in Python with asyncioWriting Redis in Python with asyncio
Writing Redis in Python with asyncio
 
Reduxing like a pro
Reduxing like a proReduxing like a pro
Reduxing like a pro
 
Tatsumaki
TatsumakiTatsumaki
Tatsumaki
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
 
Building a js widget
Building a js widgetBuilding a js widget
Building a js widget
 
ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015
 

Kürzlich hochgeladen

%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benonimasabamasaba
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...masabamasaba
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburgmasabamasaba
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastPapp Krisztián
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationJuha-Pekka Tolvanen
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 

Kürzlich hochgeladen (20)

%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 

Angular and The Case for RxJS

  • 1. Angular and The Case for RxJS Sandi K. Barr Senior Software Engineer
  • 2. Multiple values over time Cancellable with unsubscribe Synchronous or asynchronous Declarative: what, not how or when Nothing happens without an observer Separate chaining and subscription Can be reused or retried (not a spicy Promise)Observable !! ! !!
  • 3. Observable(lazy) Promise(eager) Create: new Observable((observer) => observer.next(123)); new Promise((resolve, reject) => resolve(123)); Transform: obs$.pipe(map((value) => value * 2)); promise.then((value) => value * 2); Subscribe: sub = obs$.subscribe((value) => console.log(value)); promise.then((value) => console.log(value)); Unsubscribe: sub.unsubscribe(); // implied by promise resolution https://angular.io/guide/comparing-observables#cheat-sheet
  • 4. Observable Event button.addEventListener('click', e => console.log('Clicked', e)); button.removeEventListener('click'); https://angular.io/guide/comparing-observables#observables-compared-to-events-api const clicks$ = fromEvent(button, 'click'); const subscription = clicks$.subscribe(event => console.log('Clicked', event)); subscription.unsubscribe();
  • 5.
  • 6. Observable a function that takes an Observer const subscription = observable.subscribe(observer); subscription.unsubscribe();
  • 7. Observer an object with next, error, and complete methods const observer = { next: value => console.log('Next value:', value), error: err => console.error('Error:', err), complete: () => console.log('Complete') }; const subscription = observable.subscribe(observer);
  • 8. Subscription an Observable only produces values on subscribe() A long time ago, We used to be friends, But I haven’t thought of you lately at all...
  • 9. Termination is not Guaranteed
  • 11. Cold ObsERvables The producer is created during the subscription https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339 …
  • 12. HOT ObsERvables The producer is created outside the subscription https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339 …
  • 13. SubjecT: both an observable and an observer Make a cold Observable hot https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339 …
  • 14. Subject has state Keeps a list of observers and sometimes also a number of the values that have been emitted
  • 16. @Injectable() export class TodoStoreService { private _todos: BehaviorSubject<Todo[]> = new BehaviorSubject([]); constructor(private todoHTTP: TodoHttpService) { this.loadData(); } get todos(): Observable<Todo[]> { return this._todos.asObservable(); } loadData() { this.todoAPI.getTodos() .subscribe( todos => this._todos.next(todos), err => console.log('Error retrieving Todos’) ); } } https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts Store: Observable Data Service Provide data to multiple parts of an application
  • 17. BehaviorSubject Just because you can access the value directly... https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-behavior-subject-getvalue-ts const subject = new BehaviorSubject(initialValue); // some time later… const value = subject.getValue();
  • 18. @Injectable() export class TodoStoreService { private _todo: BehaviorSubject<Todo[]> = new BehaviorSubject([]); constructor(private todoAPI: TodoHttpService) { this.loadData(); } get todos(): Observable<Todo[]> { return this._todos.asObservable(); } } https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts Protect the BehaviorSubject with asObservable() Components can subscribe after the data arrives
  • 19. @Component({ selector: 'app-todo-list', template: ` <ul> <app-todo-list-item *ngFor="let todo of todos" [todo]="todo"> </app-todo-list-item> </ul> ` }) export class TodoListComponent implements OnInit, OnDestroy { todos: Todo[]; todosSub: Subscription; constructor (private todoStore: TodoStoreService) {} ngOnInit() { this.todosSub = this.todoStore.todos.subscribe(todos => { this.todos = todos; }); } ngOnDestroy() { this.todosSub.unsubscribe(); } } LET ME HAVE THAT TODO List CLEAN UP SUBSCRIPTIONS
  • 20. @Component({ selector: 'app-todo-list', template: ` <ul> <app-todo-list-item *ngFor="let todo of todos$ | async" [todo]="todo"> </app-todo-list-item> </ul> ` }) export class TodoListComponent { todos$: Observable<Todo[]> = this.todoStore.todos; constructor (private todoStore: TodoStoreService) {} } USE THE ASYNC PIPE TO AVOID MEMORY LEAKS Subscriptions live until a stream is completed or until they are manually unsubscribed https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/components/todo-list.component.ts
  • 21. Decouple responsibilities Observable Data Service Provide data to multiple parts of an application Not the same as centralized state management Observable Data SERVICE HTTP SERVICE COMPONENTS !=
  • 22. Action method updates the store on success Store: Observable Data Service https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts addTodo(newTodo: Todo): Observable<Todo> { const observable = this.todoAPI.saveTodo(newTodo); observable.pipe( withLatestFrom(this.todos), ).subscribe(( [savedTodo, todos] ) => { this._todos.next(todos.concat(savedTodo)); }); return observable; }
  • 23. Avoid duplicating HTTP requests! https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/store/todo-store.service.ts HTTP Observables are Cold addTodo(newTodo: Todo): Observable<Todo> { const observable = this.todoAPI.saveTodo(newTodo); observable.pipe( withLatestFrom(this.todos), ).subscribe(( [savedTodo, todos] ) => { this._todos.next(todos.concat(savedTodo)); }); return observable; }
  • 24. @Injectable() export class TodoHttpService { base = 'http://localhost:3000/todos'; constructor(private http: HttpClient) { } getTodos(): Observable<Todo[]> { return this.http.get<Todo[]>(this.base); } saveTodo(newTodo: Todo): Observable<Todo> { return this.http.post<Todo>(this.base, newTodo, {headers}).pipe(share()); } } share() makes a cold Observable hot HTTP Service https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/http/todo-http.service.ts
  • 25. OPERATORS Compose complex asynchronous code in a declarative manner Pipeable Operators: take an input Observable and generate a resulting output Observable Examples: filter, map, mergeMap Creation Operators: standalone functions to create a new Observable Examples: interval, of, fromEvent, concat
  • 26. share() : refCount share() also makes Observables retry-able https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/http/todo-http.service.ts @Injectable() export class TodoHttpService { base = 'http://localhost:3000/todos'; constructor(private http: HttpClient) { } getTodos(): Observable<Todo[]> { return this.http.get<Todo[]>(this.base); } saveTodo(newTodo: Todo): Observable<Todo> { return this.http.post<Todo>(this.base, newTodo, {headers}).pipe(share()); } }
  • 27. " HOT: Share the execution ⛄ COLD: Invoke the execution
  • 28. async : Unsubscribes automatically https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-duplicate-http-component-ts But still be sure to avoid duplicating HTTP requests! @Component({ selector: 'app-todo', template: ` Total #: {{ total$ | async }} <app-todo-list [todos]="todos$ | async"></app-todo-list> ` }) export class DuplicateHttpTodoComponent { todos$ = this.http.get<Todo[]>('http://localhost:3000/todos'); total$ = this.todos$.pipe(map(todos => todos.length)); constructor(private http: HttpClient) {} }
  • 29. ngIf with | async as https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-async-as-component-ts Assign Observable values to a local variable @Component({ selector: 'app-todo', template: ` <ng-container *ngIf="todos$ | async as todos"> Total #: {{ todos.length }} <app-todo-list [todos]="todos"></app-todo-list> </ng-container> ` }) export class TodoComponent { todos$ = this.http.get<Todo[]>('http://localhost:3000/todos'); constructor(private http: HttpClient) {} }
  • 30. ngIf with| async as with ;ngElse https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-async-as-else-component-ts Show alternate block when Observable has no value @Component({ selector: 'app-todo', template: ` <ng-container *ngIf="todos$ | async as todos; else loading"> Total #: {{ todos.length }} <app-todo-list [todos]="todos"></app-todo-list> </ng-container> <ng-template #loading><p>Loading...</p></ng-template> ` }) export class TodoComponent { todos$ = this.http.get<Todo[]>('http://localhost:3000/todos'); constructor(private http: HttpClient) {} }
  • 32. Compose a series of operators Observable.prototype.pipe() import { map, filter, scan } from 'rxjs/operators'; import { range } from 'rxjs'; const source$ = range(0, 10); source$.pipe( filter(x => x % 2 === 0), map(x => x + x), scan((acc, x) => acc + x, 0) ).subscribe(x => console.log(x)); https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-pipe-ts
  • 34. map() A transformational operator Applies a projection to each value
  • 35. filter() ONE OF MANY FILTERING operatorS Filters items emitted from source
  • 36. find() A CONDITIONAL operator Finds the first match then completes
  • 37. reduce() AN Aggregate operator Emits aggregate result on completion
  • 38. Emits accumulated result at each interval scan() A TRANSFORMATIONAL operator
  • 39. @Component({ selector: 'app-todo-search', template: ` <label for="search">Search: </label> <input id="search" (keyup)="onSearch($event.target.value)"/> ` }) export class TodoSearchComponent implements OnInit, OnDestroy { @Output() search = new EventEmitter<string>(); changeSub: Subscription; searchStream = new Subject<string>(); ngOnInit() { this.changeSub = this.searchStream.pipe( filter(searchText => searchText.length > 2), // min length debounceTime(300), // wait for break in keystrokes distinctUntilChanged() // only if value changes ).subscribe(searchText => this.search.emit(searchText)); } ngOnDestroy() { if (this.changeSub) { this.changeSub.unsubscribe(); } } onSearch(searchText: string) { this.searchStream.next(searchText); } } https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/components/todo-search.component.ts TYPE AHEAD SEARCH:
  • 40. Rate-limit the input and delay the output debounceTime() A FILTERING operator
  • 41. Emits values that are distinct from the previous value distinctUntilChanged() A FILTERING operator
  • 42. AVOID NESTED SUBSCRIPTIONS pyramid shaped callback hell% % export class TodoEditComponent implements OnInit { todo: Todo; constructor(private todoStore: TodoStoreService, private route: ActivatedRoute, private router: Router) {} ngOnInit() { this.route.params.subscribe(params => { const id = +params['id']; this.todoStore.todos.subscribe((todos: Todo[]) => { this.todo = todos.find(todo => todo.id === id); }); }); } }
  • 43. Higher-Order Observables Observables that emit other Observables https://github.com/sandikbarr/rxjs-todo/blob/master/src/app/todo/components/edit/todo-edit.component.ts export class TodoEditComponent { todo$: Observable<Todo> = this.route.params.pipe( map(params => +params['id']), switchMap(id => this.todoStore.getTodoById(id)) ); constructor(private todoStore: TodoStoreService, private route: ActivatedRoute, private router: Router) {} }
  • 45. Waits for inner Observables to complete CONCAT
  • 46. Subscribe to multiple inner Observables at a time MERGE
  • 47. // using map with nested subscribe from([1, 2, 3, 4]).pipe( map(param => getData(param)) ).subscribe(val => val.subscribe(data => console.log(data))); // using map and mergeAll from([1, 2, 3, 4]).pipe( map(param => getData(param)), mergeAll() ).subscribe(val => console.log(val)); // using mergeMap from([1, 2, 3, 4]).pipe( mergeMap(param => getData(param)) ).subscribe(val => console.log(val)); mergeMap() Higher-order transformation operator Luuk Gruijs: https://medium.com/@luukgruijs/understanding-rxjs-map-mergemap-switchmap-and-concatmap-833fc1fb09ff Projects each source value into an Observable that is merged into the output Observable
  • 48. Also provide the latest value from another Observable withLatestFrom() combineLatest() Updates from the latest values of each input Observable https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-combinelatest-withlatestfrom-ts combineLatest( [notifications$, otherPrimaryActiities$] ) .subscribe({ next: ( [notification, otherPrimaryActivity] ) => { // fires whenever notifications$ _or_ otherPrimaryActivities$ updates // but not until all sources have emitted at least one value } }); notifications$.pipe( withLatestFrom(mostRecentUpdates$) ) .subscribe({ next: ( [notification, mostRecentUpdate] ) => { // fires only when notifications$ updates and includes latest from mostRecentUpdates$ } });
  • 49. @Component({ selector: 'app-many-subscriptions', template: `<p>value 1: {{value1}}</p> <p>value 2: {{value2}}</p> <p>value 3: {{value3}}</p>` }) export class SubscriberComponent implements OnInit, OnDestroy { value1: number; value2: number; value3: number; destroySubject$: Subject<void> = new Subject(); constructor(private service: MyService) {} ngOnInit() { this.service.value1.pipe( takeUntil(this.destroySubject$) ).subscribe(value => { this.value1 = value; }); this.service.value2.pipe( takeUntil(this.destroySubject$) ).subscribe(value => { this.value2 = value; }); this.service.value3.pipe( takeUntil(this.destroySubject$) ).subscribe(value => { this.value3 = value; }); } ngOnDestroy() { this.destroySubject$.next(); } } Always Bring Backup with this one weird trick USE A SUBJECT TO Complete STREAMS https://gist.github.com/sandikbarr/36bb0bf0b99a82a74be92aba1b1d0482#file-weird-trick-takeuntil-ts-L1
  • 50. tap() a UTILITY operator Perform side effects and return the stream unchanged
  • 51. catchError() an error handling operator Catch errors in the stream and return a new Observable
  • 52. catchError() An error can terminate a stream and send the error to the error() callback, or the stream can be allowed to continue if piped through catchError().
  • 55. Custom Functions to Add and Remove Event Handlers generate interval fromEventPattern fromEvent Create a New Observable Sequence That Works Like a for-Loop That Never Does Anything That Repeats a Value That Throws an Error That Completes From an Event From a Promise That Iterates That Emits Values on a Timer Decided at Subscribe Time Based on a Boolean Condition Over Values in a Numeric Range Over Values in an Iterable or Array-like Sequence Over Arguments With an Optional Delay Using Custom Logic Of Object Key/Values repeat throwError EMPTY NEVER from pairs range of timer iif defer usingThat Depends on a Resource
  • 56. Troubleshooting Has this Observable been subscribed to? How many Subscriptions does this Observable have? When does this Observable complete? Does it complete? Do I need to unsubscribe from this Observable?
  • 57. THANKS! example code: https://github.com/sandikbarr/rxjs-todo slides: https://www.slideshare.net/secret/FL6NONZJ7DDAkf