Se ha denunciado esta presentación.
Se está descargando tu SlideShare. ×

Reactive frontends with RxJS and Angular

Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Cargando en…3
×

Eche un vistazo a continuación

1 de 45 Anuncio

Reactive frontends with RxJS and Angular

Descargar para leer sin conexión

SpringOne Platform 2017
Sergi Almar, Independent

"Reactive programming has changed the way we develop modern applications. If you are a Java backend developer you might be already familiar with this paradigm and the new Spring 5 support. But what about the frontend? We want to build clean, testable, and scalable apps. The good news is that we can reuse the knowledge, the concepts are universal.

In this presentation we’ll introduce the fundamentals of RxJS and see how to manage data streams like UI events, async HTTP requests, WebSockets / SSE…in a uniform way. Let RxJS do the heavy lifting.

Angular embraces and makes heavy use of RxJS, we’ll see how to use them together with practical examples on common problems."

SpringOne Platform 2017
Sergi Almar, Independent

"Reactive programming has changed the way we develop modern applications. If you are a Java backend developer you might be already familiar with this paradigm and the new Spring 5 support. But what about the frontend? We want to build clean, testable, and scalable apps. The good news is that we can reuse the knowledge, the concepts are universal.

In this presentation we’ll introduce the fundamentals of RxJS and see how to manage data streams like UI events, async HTTP requests, WebSockets / SSE…in a uniform way. Let RxJS do the heavy lifting.

Angular embraces and makes heavy use of RxJS, we’ll see how to use them together with practical examples on common problems."

Anuncio
Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

Similares a Reactive frontends with RxJS and Angular (20)

Anuncio

Más de VMware Tanzu (20)

Más reciente (20)

Anuncio

Reactive frontends with RxJS and Angular

  1. 1. Reactive Frontends with RxJS and Angular Sergi Almar @sergialmar 1
  2. 2. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reactive efforts in Spring 2 Spring framework Spring Boot Project Reactor Spring Security Spring Data Spring Cloud Spring Integration …
  3. 3. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Agenda 3 • RxJS • Obervables • Operators • Combining observables • Error handling • Angular • Managing subscriptions • HttpClient • Reactive Forms • WebSockets /SSE
  4. 4. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 4
  5. 5. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Language support 5
  6. 6. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Async in web apps 6 • DOM Event (0-N values) • AJAX (1 value) • WebSocket (0-N values) • Server Sent Event (0-N values) • Animation (0-N values)
  7. 7. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reactive programming 7 Pros • Clean and understandable code • Focus on the result • Complex operations out of the box • Abort processing of data when you don’t need it (cancel) Cons • More difficult to debug • Learning curve
  8. 8. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 8 Observable.of(...) Factory Methods Observable.from(...) Observable.fromEvent(...) Observable.interval(...) ...
  9. 9. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 9 Creating an Observable const welcome$ = Observable.create((subscriber) => { subscriber.next('Welcome'); subscriber.next('to'); subscriber.next('S1P'); subscriber.next('!'); subscriber.complete(); });
  10. 10. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 10 Observer subscribe notification
  11. 11. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Observer 11 export interface Observer<T> { closed?: boolean; next: (value: T) => void; error: (err: any) => void; complete: () => void; } var observer = { next: x => console.log('Next value: ' + x), error: err => console.error('Error: ' + err), complete: () => console.log('Complete notification') };
  12. 12. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Observer 12 Observable.interval(1000) Producer Consumer.subscribe(val => console.log(val)) .operator(...) .operator(...) .operator(...) Pipeline
  13. 13. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Unsubscribe 13 const mouseMove$ = Observable.fromEvent(document, 'mousemove'); const subscription = mouseMove$.subscribe(console.log); … subscription.unsubscribe(); - Release resources allocated by the observable - No more events sent to the registered observer
  14. 14. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Operators 14 create fromEvent range … Creation map scan switchMap … Transformation debounce filter take … Filtering concat merge startWith … Combination catch retry retryWhen Error Handling share publish multicast Multicast do / tap delay toPromise … Utility every defaultIfEmpty Conditional
  15. 15. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Lettable Operators 15 <T, R>(source: Observable<T>) => Observable<R> Observable.interval(1000) .pipe( filter(x => x % 2 === 0), map(x => x * x), take(5) ) .subscribe(x => console.log(x))
  16. 16. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 16
  17. 17. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Combining Observables: merge 17 const spring$ = Observable.of('spring', 'spring-boot', ‘spring-data'); const cloud$ = Observable.of('cloud-native', 'cloudfoundry', 'bosh'); Observable.merge(spring$, cloud$).subscribe(console.log);
  18. 18. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 1818 Spring Spring spring$ cloud$ Spring Boot Spring Boot Cloud Native Cloud Native Spring Data Spring Data Cloud Foundry Cloud Foundry Bosh Bosh merge(spring$, cloud$)
  19. 19. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ switchMap 19 Observable.of(1, 2, 3) .pipe( switchMap(num => Observable.of(num, num * num)) ).subscribe(console.log); 1 switchMap(num => Observable.of(num, num * num) 1 1 2 2 4 3 3 9
  20. 20. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ forkJoin 20 1 const users$ = Observable.forkJoin( findUser(1), findUser(2), findUser(3) ); ... users$.subscribe(console.log); 2 3 fork join
  21. 21. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ fork forkJoin 21 1 const users$ = Observable.forkJoin( findUser(1), findUser(2), findUser(3) ); users$.subscribe(console.log); 2 3 join
  22. 22. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ join forkJoin 22 1 const users$ = Observable.forkJoin( findUser(1), findUser(2), findUser(3) ); users$.subscribe(console.log); fork 1 2 3 2 3
  23. 23. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ join forkJoin 23 1 const users$ = Observable.forkJoin( findUser(1), findUser(2), findUser(3) ); users$.subscribe(console.log); fork 2 3[ ], ,
  24. 24. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Errors 24 • When an error occurs the observable is cancelled and no data passes though • Errors are propagated to the downstream observers Observable.range(1, 20) .pipe( map(num => { if (num === 13) { throw new Error('I don't like 13'); } return num; }) )
  25. 25. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Remember the Observer? 25 export interface Observer<T> { closed?: boolean; next: (value: T) => void; error: (err: any) => void; complete: () => void; }
  26. 26. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Errors 26 • When an error occurs the observable is cancelled and no data passes though • Errors are propagated to the downstream observers Observable.range(1, 20) .pipe( map(num => { if (num === 13) { throw new Error('I don't like 13'); } return num; }) ).subscribe( num => console.log('Got num ' + num), err => console.log('Upssss...' + err)); map 1 1 13 X … …
  27. 27. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ catchError 27 Observable.range(1, 20) .pipe( map(num => { if (num === 13) { throw new Error('I don't like 13'); } return num; }), catchError(err => Observable.range(100, 3)) ) .subscribe(console.log); Output: 1 2 3 4 5 6 7 8 9 10 11 12 100 111 112
  28. 28. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Angular 28 RxJS can be used with any framework Angular has reactivity at its core, leveraging RxJS - HttpClient - Reactive Forms - Router - Component communication
  29. 29. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Unsubscribing 29 @Component({ selector: 'app-mouse-position', template: '<span>{{mousePosition}}</span>' }) export class MousePositionComponent implements OnInit, OnDestroy { ngOnInit() { } ngOnDestroy() { } }
  30. 30. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Unsubscribing 30 @Component({ selector: 'app-mouse-position', template: '<span>{{mousePosition}}</span>' }) export class MousePositionComponent implements OnInit, OnDestroy { ngOnInit() { } ngOnDestroy() { } } const mouseDown$ = Observable.fromEvent(document, 'mousemove') .pipe( map(val => `${val.offsetX} ${val.offsetY}`) ); Define Observable
  31. 31. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Unsubscribing 31 @Component({ selector: 'app-mouse-position', template: '<span>{{mousePosition}}</span>' }) export class MousePositionComponent implements OnInit, OnDestroy { mousePosition: String; mouseSubscription: Subscription; ngOnInit() { } ngOnDestroy() { } } this.mouseSubscription = mouseDown$ .subscribe((pos: String) => this.mousePosition = pos); const mouseDown$ = Observable.fromEvent(document, 'mousemove') .pipe( map(val => `${val.offsetX} ${val.offsetY}`) ); Subscribe
  32. 32. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Unsubscribing 32 @Component({ selector: 'app-mouse-position', template: '<span>{{mousePosition}}</span>' }) export class MousePositionComponent implements OnInit, OnDestroy { mousePosition: String; mouseSubscription: Subscription; ngOnInit() { } ngOnDestroy() { } } this.mouseSubscription = mouseDown$ .subscribe((pos: String) => this.mousePosition = pos); const mouseDown$ = Observable.fromEvent(document, 'mousemove') .pipe( map(val => `${val.offsetX} ${val.offsetY}`) ); this.mouseSubscription.unsubscribe();Unsubscribe
  33. 33. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Async pipe 33 @Component({ selector: 'app-mouse-position', template: '<span>{{mousePosition$ | async}}</span>' }) export class MousePositionComponent implements OnInit { mousePosition$: Observable<string>; ngOnInit() { this.mousePosition$ = fromEvent(document, 'mousemove') .pipe( map(val => `${val.offsetX} ${val.offsetY}`) ); } } No subscription management
  34. 34. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HttpClient 34 - Simple API - Strong typing of request and response objects - Support for interceptors
  35. 35. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HttpClient 35 get<T>(url: string, options?: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: 'body'; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: 'json'; withCredentials?: boolean; }): Observable<T>; httpClient.get<User>('http://localhost:8080/user/1') .subscribe((user: User) => console.log('User: ' + user.name));
  36. 36. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Retry 36 - Retry the failed stream - Propagates the error downstream if not successful httpClient.get<User>('http://localhost:8080/user/1') .pipe( retry(3) ) httpClient.get<User>('http://localhost:8080/user/1') .pipe( retryWhen(errors => errors.pipe(delay(1000).take(3))) )
  37. 37. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reactive Forms 37 • Two options to build forms • Reactive and template-driven forms • Monitor changes by subscribing to one of the form control properties  this.favoriteConference.valueChanges this.favoriteConference.statusChanges (valid / invalid)<input formControllName=“favoriteConference” ..>
  38. 38. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Server Sent Events 38 Wrapping an EventSource in an Observable const eventSource$ = Observable.create(observer => { const source = new EventSource('http://localhost:8080/search-push'); source.addEventListener('message', event => observer.next(event)); } ); * EventSource added in typings.d.ts
  39. 39. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ WebSocket 39 Wrapping a WebSocket in an Observable const ws$ = Observable.create(observer => { const source = new WebSocket(‘ws://localhost:8080/search-push'); source.addEventListener('message', event => observer.next(event)); } );
  40. 40. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ WebSocket 40 Wrapping a WebSocket in an Observable ws$.subscribe(...) new WebSocket(...) ws$.subscribe(...) new WebSocket(...) ws$.subscribe(...) new WebSocket(...)
  41. 41. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Subscription sharing 41 const ws$ = Observable.create(observer => { const source = new WebSocket(‘ws://localhost:8080/search-push'); source.addEventListener('message', event => observer.next(event)); } ) .share();
  42. 42. DEMO Putting all together
  43. 43. Q&A Thank you! @sergialmar 43 #springone@s1p
  44. 44. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 44

×