Commit c38fa0ba authored by micha's avatar micha
Browse files

progress comparison

parent 4067cd82
......@@ -2,6 +2,8 @@
## Intro
![Combination operators comparison - screenshot](./assets/images/Reactive-architecture-and-ux-patterns_angular_combination-operators-comparison-solution-screenshot_michael-hladky.png)
In the start component you will find a screen split into 2 sections.
Left and right. In the center
we have 3 boxed displaying the result of the named strategies combine, withLatest and zip.
......@@ -29,6 +31,16 @@ we have 3 boxed displaying the result of the named strategies combine, withLates
</div>
```
In the components class we have the displayed observables present as Subjects:
```typescript
export class SolutionComparisonComponent implements AfterViewInit, OnDestroy {
// ...
clickResultCombine$ = new Subject<string>();
clickResultWithLatest$ = new Subject<string>();
clickResultZip$ = new Subject<string>();
```
Also, 2 Observables are already setup in the `ngAfterViewInit` hook, `clickPosX$` and `elemWith$`.
```typescript
......
# Combination Operators Comparison - Solution
To create the different combinations we compose and subscribe to int in the ngAfterViewInit section.
**Component**
```Typescript
import {combineLatest, zip} from 'rxjs';
import {withLatestFrom, map} from 'rxjs/operators';
ngAfterViewInit(): void {
//...
this.subscription.add(
combineLatest([clickPosX$, elemWith$])
.pipe(
map(([posX, width]) => this.getSideOfClick(posX, width)),
).subscribe(this.clickResultCombine$)
);
this.subscription.add(clickPosX$
.pipe(
withLatestFrom(elemWith$),
map(([posX, width]) => this.getSideOfClick(posX, width))
).subscribe(this.clickResultWithLatest$)
);
this.subscription.add(zip(clickPosX$, elemWith$)
.pipe(
map(([posX, width]) => this.getSideOfClick(posX, width))
).subscribe(this.clickResultZip$)
);
}
```
This gives us rendered values in the template, and we can compare the different behaviors.
For the given example we discover the following behavior:
- All behaviors need the first value from both, the click position and the div width
- `combineLatest` calculates the click position even if we only resize the screen.
We get an incorrectly calculate click side in several cases, also the calculation on width change is not necessary here.
- `withLatestFrom` perfectly loves the calculation of the click position. It takes the last value from the divs width and only calculates on click events.
- `zip` gives the worst result here. As we calculate the position by pairing click position and width in the order we result in behavior not matching the user's clicks and resizes.
## Subscription and Subjects
Let's have a closer look at how we connected the values to the template.
In our component we 1 property `subscription` and 3 predefined Subjects named `clickResultCombine$`, `clickResultWithLatest$` and `clickResultZip$`
```typescript
subscription = new Subscription();
clickResultCombine$ = new Subject<string>();
clickResultWithLatest$ = new Subject<string>();
clickResultZip$ = new Subject<string>();
constructor(private elemRef: ElementRef) {
}
ngAfterViewInit(): void {
// ...
}
```
In our the `ngAfterViewInit` method we forward the composed values in the subscription:
```Typescript
this.subscription.add(
combineLatest([clickPosX$, elemWith$])
.pipe(
map(([posX, width]) => this.getSideOfClick(posX, width)),
)
.subscribe(this.clickResultCombine$)
);
```
Here we use the `add` method of our subscription instance.
`this.subscription.add(observable$.subscribe());`
This allows us to add other subscriptions to the existing one by adding it as a nested subscription.
If we call unsubscribe on the main subscription we also unsubscribe from all added subscriptions.
In the subscribe call we se the following `.subscribe(this.clickResultCombine$)`.
`clickResultCombine$` is a Subject. It exposes the `next`, `error`, and `complete` method.
`subscribe` takes and Observer object which maintains the methods `next`, `error`, and `complete`.
Due to the described signatures we could write the following code:
```typescript
observable$
.subscribe({
next(nextValue) { subject1.next(nextValue) },
error(errorValue) { subject1.next(errorValue) },
complete(nextValue) { subject1.complete() }
});
```
In a way shorter form like this:
```typescript
observable$
.subscribe(subject1)
```
# Combination Operators Comparison
As we did quite a lot od technical stuff it's time to relax and do some playful stuff.
As we did quite a lot of technical stuff it's time to relax and do some playful stuff.
Let's see how the combination operators compare to each other. :)
In this exercise we compare the different operators:
......@@ -15,13 +15,13 @@ We exclude `forkJoin` as we will process ongoing streams.
Also, the operators `zipAll` as well as `combineAll` are left out.
We will discuss `All` operators in a different chapter.
Let's take a closer look to the used RxJs functions.
We have 2 different types, operators and creators functions.
Let's take a closer look at the used RxJs functions.
We have 2 different types, operators, and creators functions.
The operators of the listed functions always have the following signature:
`(Observable<T>) => (Observable<R>) => Observable<[T, R]>`
In version `7` of RxJS there was a refactoring.
In version `7` of RxJS, there was a refactoring.
The operator versions of `zip` and `combineLatest` got deprecated and the
new operators `zipWith` and `combineLatestWith` got introduced.
......@@ -42,7 +42,7 @@ const result2$ = source1$.pipe(
);
```
This 2 operators are siblings, one of them, `combineLates` is the creation function.
These 2 operators are siblings, one of them, `combineLates` is the creation function.
The other, `combineLatestWith` is the operator.
......@@ -62,7 +62,7 @@ const result2$ = source1$.pipe(
);
```
This 2 operators are similar to `combineLatest` siblings,
These 2 operators are similar to `combineLatest` siblings,
The creation function is `zip` and the operator is `zipWith`.
**withLatestFrom**
......@@ -79,10 +79,10 @@ const result1$ = source1$.pipe(
);
```
For this behaviour we only have the operator.
For this behavior, we only have the operator.
This creation function would not be that intuitive to use.
To get some more overview of the behaviour lets start a small exercise.
To get some more overview of the behavior lets to start a small exercise.
import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core';
import {combineLatest, fromEvent, ReplaySubject, Subscription, zip} from "rxjs";
import {combineLatest, fromEvent, ReplaySubject, Subject, Subscription, zip} from "rxjs";
import {map, shareReplay, startWith, tap, withLatestFrom} from "rxjs/operators";
@Component({
......@@ -35,9 +35,9 @@ export class SolutionComparisonComponent implements AfterViewInit, OnDestroy {
@ViewChild('box')
boxViewChild;
clickResultCombine$ = new ReplaySubject<string>(1);
clickResultWithLatest$ = new ReplaySubject<string>(1);
clickResultZip$ = new ReplaySubject<string>(1);
clickResultCombine$ = new Subject<string>();
clickResultWithLatest$ = new Subject<string>();
clickResultZip$ = new Subject<string>();
constructor(private elemRef: ElementRef) {
}
......
import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core';
import {fromEvent, ReplaySubject, Subscription} from "rxjs";
import {fromEvent, ReplaySubject, Subject, Subscription} from "rxjs";
import {map, tap} from "rxjs/operators";
@Component({
......@@ -32,11 +32,11 @@ export class StartComparisonComponent implements AfterViewInit, OnDestroy {
subscription = new Subscription();
@ViewChild('box')
boxViewChild;
boxViewChild
clickResultCombine$ = new ReplaySubject<string>(1);
clickResultWithLatest$ = new ReplaySubject<string>(1);
clickResultZip$ = new ReplaySubject<string>(1);
clickResultCombine$ = new Subject<string>();
clickResultWithLatest$ = new Subject<string>();
clickResultZip$ = new Subject<string>();
constructor(private elemRef: ElementRef) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment