Commit 0eb57adc authored by micha's avatar micha
Browse files

add text extend overview

parent a6ab8a67
# ReactiveArchitectureAndUXPatterns
<!-- Course Image -->
![](./assets/images/Reactive-architecture-and-ux-patterns_angular_michael-hladky.png)
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.4.
<!-- Course Title -->
# Reactive Architecture and UX Patterns
## Development server
<!-- Course Tagline -->
#### Getting the tools and understanding to craft fully reactive angular applications!
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
<!-- Course Description -->
## Code scaffolding
<!-- Course Description Intro Images -->
![](./assets/images/Reactive-architecture-and-ux-patterns_secondary_angular_michael-hladky.png)
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
<!-- Course Description Text -->
## Overview
## Build
Welcome to my course! My name is Michael and I will lead you through this course.
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
The title of this course is reactive architecture and UX patterns.
As those are pretty broad terms let be elaborate a bit on the scope and target audience.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
### Level of this Course
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
First the level. For this course, you definitely need some fundamental knowledge about RxJS,
and you should use it regularly in your Angular projects.
General terms like subscription handling, multi-casting, or hot/cold Observables should at least ring a little bell.
## Further help
If this is not the case no big deal. As this, in an online course, you can consume it as fast or slow as you like.
It's all about fun and explores new things, so the most important to enjoy! :)
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
### Content and Learning Goals
![](./assets/images/Reactive-architecture-and-ux-patterns_overview_angular_michael-hladky.png)
This course starts with the fundamentals of the different operator groups we will use,
and points out some interesting details.
With that in mind, we will start to work on our first patterns.
Exercises we will master are:
- Deriving state and combination operators
- Overview of combination the operators
- HTTP fetching and with `forkJoin`
- Avoid over-fetching by introducing state into our Http service
- Continuously deriving state with `combineLatest`
- Reduce computations and understand data normalization
- Opt-In Updates and `withLatestFrom`
- Reactive State-Management
- State aggregation
- State selection
- Architecture Patterns
- Higher-Order Observables
- Overview of the different flattening strategies (merge, concat, exhaust, switch)
- Usage in the user Interface
- Usage in business logic or data layer interaction like effects/epic of REDUX
- Error-Handling
- In-depth understanding of error handling
- How to encapsulate error-prone code
- Comparison of the different retry & repeat mechanisms
Let’s jump right in and start with the first section.
......@@ -44,18 +44,18 @@ If we understand every of those "broken parts" we are intuitively able to unders
Within this set of lessons we will walk through the following exercises:
- [] We start we a simple setup where we derive data in our component directly over HTTP requests by using `forkJoin`
- [ ] We start we a simple setup where we derive data in our component directly over HTTP requests by using `forkJoin`
- with this architecture we realize, we quickly run into the problem of over-fetching
- To solve it we refactor the give HTTP service to get more control over when we fetch the data
- [ ] To solve it we refactor the give HTTP service to get more control over when we fetch the data
- this reviles one of the special behaviours of `frokJoin` and we need to rethink it usage
- [] We learn the difference of `forkJoin` and `combineLatest`
- [ ] We learn the difference of `forkJoin` and `combineLatest`
- this knowledge helps us to refactor the service and component.
- As we go we start to introduce more features into our UI
- [ ] As we go we start to introduce more features into our UI
- again we run into a problem, this time over-rendering.
- [] To understand the problem we learn about the terms `Normalized` and `Denormalized` data
- [ ] To understand the problem we learn about the terms `Normalized` and `Denormalized` data
- by using `zip` for our calculation we are able to solve the problem of over-rendering
- [] As it was quite technical so far we learn about `withLatestFrom` with a more playful example
- [ ] As it was quite technical so far we learn about `withLatestFrom` with a more playful example
- by doing so we understand the concept of `promary` and `secondary` streams
- [] With a fresh and open mine we think about those concepts in combination with a UX Pattern called `opt-in updates`
- [ ] With a fresh and open mine we think about those concepts in combination with a UX Pattern called `opt-in updates`
- to give a better experience to our users we implement this pattern in our example
import {
AfterViewInit,
ChangeDetectionStrategy,
Component,
OnDestroy,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import {combineLatest, fromEvent, ReplaySubject, Subject, Subscription} from "rxjs";
import {map, startWith, withLatestFrom} from "rxjs/operators";
import {AfterViewInit, Component, OnDestroy, ViewChild} from '@angular/core';
import {combineLatest, fromEvent, ReplaySubject, Subscription, zip} from "rxjs";
import {map, shareReplay, startWith, tap, withLatestFrom} from "rxjs/operators";
@Component({
selector: 'withLatestFrom',
......@@ -15,13 +8,19 @@ import {map, startWith, withLatestFrom} from "rxjs/operators";
<div #box class="box">
<div class="separation">
</div>
<div class="click-result">
{{clickResult$ | async}}
combineLatest:
{{clickResultCombine$ | async}}
</div>
<div class="click-result">
withLatestFrom:
{{clickResultWithLatest$ | async}}
</div>
<div class="click-result">
zip:
{{clickResultZip$ | async}}
</div>
</div>
`,
......@@ -29,16 +28,17 @@ import {map, startWith, withLatestFrom} from "rxjs/operators";
.box {
position: relative;
width: 100%;
height: 300px;
height: 400px;
border: 1px solid darkgray;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: lightcyan;
}
.separation {
height: 300px;
height: 400px;
width: 50%;
position: absolute;
left: 0px;
......@@ -48,7 +48,7 @@ import {map, startWith, withLatestFrom} from "rxjs/operators";
}
.click-result {
width: 200px;
width: 250px;
height: 100px;
line-height: 100px;
text-align: center;
......@@ -65,23 +65,41 @@ export class SolutionWithLatestFromComponent implements AfterViewInit, OnDestro
@ViewChild('box')
boxViewChild;
clickResult$ = new ReplaySubject<string>(1);
clickResultCombine$ = new ReplaySubject<string>(1);
clickResultWithLatest$ = new ReplaySubject<string>(1);
clickResultZip$ = new ReplaySubject<string>(1);
ngAfterViewInit(): void {
const clickPosX$ = fromEvent(this.boxViewChild.nativeElement, 'click').pipe(
map((e) => e['offsetX'])
map((e) => e['offsetX']),
shareReplay(1)
);
const elemWith$ = fromEvent(window, 'resize').pipe(
map(() => this.boxViewChild.nativeElement.getBoundingClientRect().width),
startWith(this.boxViewChild.nativeElement.getBoundingClientRect().width)
startWith(this.boxViewChild.nativeElement.getBoundingClientRect().width),
shareReplay(1)
);
this.subscription = clickPosX$
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.clickResult$);
).subscribe(this.clickResultWithLatest$)
);
this.subscription.add(zip(clickPosX$, elemWith$)
.pipe(
map(([posX, width]) => this.getSideOfClick(posX, width))
).subscribe(this.clickResultZip$)
);
}
......
......@@ -12,7 +12,16 @@ import {map, startWith, withLatestFrom} from "rxjs/operators";
</div>
<div class="click-result">
{{clickResult$ | async}}
combineLatest
{{clickResultCombine$ | async}}
</div>
<div class="click-result">
withLatestFrom
{{clickResultWithLatest$ | async}}
</div>
<div class="click-result">
zip
{{clickResultZip$ | async}}
</div>
</div>
......@@ -22,16 +31,17 @@ import {map, startWith, withLatestFrom} from "rxjs/operators";
.box {
position: relative;
width: 100%;
height: 300px;
height: 400px;
border: 1px solid darkgray;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: lightcyan;
}
.separation {
height: 300px;
height: 400px;
width: 50%;
position: absolute;
left: 0px;
......@@ -41,7 +51,7 @@ import {map, startWith, withLatestFrom} from "rxjs/operators";
}
.click-result {
width: 200px;
width: 250px;
height: 100px;
line-height: 100px;
text-align: center;
......@@ -58,7 +68,9 @@ export class StartWithLatestFromComponent implements AfterViewInit, OnDestroy {
@ViewChild('box')
boxViewChild;
clickResult$ = new ReplaySubject<string>(1);
clickResultCombine$ = new ReplaySubject<string>(1);
clickResultWithLatest$ = new ReplaySubject<string>(1);
clickResultZip$ = new ReplaySubject<string>(1);
constructor() {
......
......@@ -16,9 +16,9 @@ export function mergeListsAndItems(lists: List[], items: Item[]): Array<JoinedIt
);
}
export function upsertEntities<T>(oldEntities: T[], newLists: T[], id: string): T[] {
const insertLists = newLists.filter(nL => !oldEntities.find(oL => oL[id] === nL[id]))
const updateLists = newLists.filter(nL => oldEntities.find(oL => oL[id] === nL[id]))
export function upsertEntities<T>(oldEntities: T[], newEntities: T[], id: string): T[] {
const insertLists = newEntities.filter(nL => !oldEntities.find(oL => oL[id] === nL[id]))
const updateLists = newEntities.filter(nL => oldEntities.find(oL => oL[id] === nL[id]))
return insertLists.concat(
oldEntities.map(
......
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