diff --git a/.editorconfig b/.editorconfig index 6e87a003da..493aaa0524 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,5 @@ indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true -[*.md] +[*.{md,mdx}] max_line_length = off -trim_trailing_whitespace = false diff --git a/README.md b/README.md index 025c21cb3f..4cae28f890 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![rx-angular logo](https://raw.githubusercontent.com/rx-angular/rx-angular/main/docs/images/rx-angular_logo.png)](https://www.rx-angular.io/) +[![rx-angular logo](https://raw.githubusercontent.com/rx-angular/rx-angular/main/docs/images/rx-angular_logo.png)](https://rx-angular.io/) # RxAngular ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) @@ -7,10 +7,10 @@ performance and template rendering. RxAngular is divided into different packages: -- [📦@rx-angular/cdk](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/README.md) -- [📦@rx-angular/eslint-plugin](https://github.com/rx-angular/rx-angular/tree/main/libs/eslint-plugin/README.md) -- [📦@rx-angular/state](https://github.com/rx-angular/rx-angular/tree/main/libs/state/README.md) -- [📦@rx-angular/template](https://github.com/rx-angular/rx-angular/tree/main/libs/template/README.md) +- [📦@rx-angular/cdk](https://rx-angular.io/docs/cdk) +- [📦@rx-angular/eslint-plugin](https://rx-angular.io/docs/eslint-plugin) +- [📦@rx-angular/state](https://rx-angular.io/docs/state) +- [📦@rx-angular/template](https://rx-angular.io/docs/template) Used together, you get a powerful tool for developing high-performance angular applications with or without NgZone. @@ -63,17 +63,17 @@ This repository holds a set of helpers to create **fully reactive** as well as * ## Packages -Find details in the linked readme files below for installation and setup instructions, examples and resources. +Find details in the links to the official docs below for installation and setup instructions, examples and resources. -- [📦@rx-angular/cdk](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/README.md) - Component Development Kit -- [📦@rx-angular/eslint-plugin](https://github.com/rx-angular/rx-angular/tree/main/libs/eslint-plugin/README.md) - ESLint Plugin -- [📦@rx-angular/state](https://github.com/rx-angular/rx-angular/tree/main/libs/state/README.md) - Imperative & Reactive Component State-Management -- [📦@rx-angular/template](https://github.com/rx-angular/rx-angular/tree/main/libs/template/README.md) - High-Performance Non-Blocking Rendering +- [📦@rx-angular/cdk](https://rx-angular.io/docs/cdk) - Component Development Kit +- [📦@rx-angular/eslint-plugin](https://rx-angular.io/docs/eslint-plugin) - ESLint Plugin +- [📦@rx-angular/state](https://rx-angular.io/docs/state) - Imperative & Reactive Component State-Management +- [📦@rx-angular/template](https://rx-angular.io/docs/template) - High-Performance Non-Blocking Rendering ## Version Compatibility -| Angular | RxJS | @rx-angular/state | @rx-angular/template | @rx-angular/cdk | -|------------------------|----------------------|-------------------|----------------------|---------------------| +| Angular | RxJS | @rx-angular/state | @rx-angular/template | @rx-angular/cdk | +| ---------------------- | -------------------- | ----------------- | -------------------- | ------------------- | | `14` | `^7.4.0` | `> 1.4.6` | `> 1.0.0-beta.29` | `> 1.0.0-alpha.10` | | `^12.0.0` or `^13.0.0` | `^6.5.5` or `^7.4.0` | `> 1.4.6` | `> 1.0.0-beta.29` | `> 1.0.0-alpha.10` | | `^11.0.0` | `^6.5.5` | `<= 1.4.6` | `<= 1.0.0-beta.29` | `<= 1.0.0-alpha.10` | diff --git a/apps/demos/src/app/features/template/render-callback/renderCallback.md b/apps/demos/src/app/features/template/render-callback/renderCallback.md index 7cba5956bd..efa0f06b95 100644 --- a/apps/demos/src/app/features/template/render-callback/renderCallback.md +++ b/apps/demos/src/app/features/template/render-callback/renderCallback.md @@ -1,56 +1,62 @@ ### Using the RenderCallback - The RenderCallback notifies users about when the `LetDirective` "rendered" the latest values of the - active template. - At the time the `rendered` callback emits, the DOM should be already updated with the latest changes connected - to this instance. - The callback will emit the latest value rendered to the template. - - Since structural directives currently do not support `@Output` bindings, developers have to use other mechanics - to access this event. - Beyond the traditional approach the `LetDirectives` offers an input property as renderCallback. - This enables developers to bind a `NextObserver` (e.g. `Subject`) to the `LetDirective`which will emit after - rendering happened. - - You can choose between using the [Template syntax](https://angular.io/guide/template-syntax), injecting the - `LetDirective` as `@ViewChild()` and subscribe the event manually or providing a RenderCallback on your own. - - Please note that due to the built-in - [coalescing][https://github.com/rx-angular/rx-angular/blob/main/libs/template/docs/concepts.md] can cause this - callback different in situations where multiple `LetDirectives` are used to render the same - `Component`. Make sure to subscribe to every instance in your component to avoid missing render - notifications. - - #### RenderCallback Input - ```html - -
- {{ content }} -
-
- ``` - ```ts - // inside component: - readonly renderCallback$ = new Subject(); - ``` - #### Template syntax - ```html - - -
- {{ content }} -
-
- ``` - #### ViewChild - ```html -
- {{ content }} -
- ``` - ```ts - // inside of your component - \@ViewChild(LetDirective) rxLet: LetDirective; - this.rxLet.rendered.subscribe(value => console.log('afterRender', value)); - ``` + +The RenderCallback notifies users about when the `LetDirective` "rendered" the latest values of the +active template. +At the time the `rendered` callback emits, the DOM should be already updated with the latest changes connected +to this instance. +The callback will emit the latest value rendered to the template. + +Since structural directives currently do not support `@Output` bindings, developers have to use other mechanics +to access this event. +Beyond the traditional approach the `LetDirectives` offers an input property as renderCallback. +This enables developers to bind a `NextObserver` (e.g. `Subject`) to the `LetDirective`which will emit after +rendering happened. + +You can choose between using the [Template syntax](https://angular.io/guide/template-syntax), injecting the +`LetDirective` as `@ViewChild()` and subscribe the event manually or providing a RenderCallback on your own. + +Please note that due to the built-in +[coalescing][https://rx-angular.io/docs/template/concepts] can cause this +callback different in situations where multiple `LetDirectives` are used to render the same +`Component`. Make sure to subscribe to every instance in your component to avoid missing render +notifications. + +#### RenderCallback Input + +```html + +
{{ content }}
+
+``` + +```ts +// inside component: +readonly renderCallback$ = new Subject(); +``` + +#### Template syntax + +```html + + +
{{ content }}
+
+``` + +#### ViewChild + +```html +
{{ content }}
+``` + +```ts +// inside of your component +\@ViewChild(LetDirective) rxLet: LetDirective; +this.rxLet.rendered.subscribe(value => console.log('afterRender', value)); +``` diff --git a/apps/demos/src/app/features/tutorials/basics/Readme.md b/apps/demos/src/app/features/tutorials/basics/README.md similarity index 99% rename from apps/demos/src/app/features/tutorials/basics/Readme.md rename to apps/demos/src/app/features/tutorials/basics/README.md index 3a46791e62..bf478916f2 100644 --- a/apps/demos/src/app/features/tutorials/basics/Readme.md +++ b/apps/demos/src/app/features/tutorials/basics/README.md @@ -24,6 +24,7 @@ There is a background process running in the child component. The input value fr Furthermore, there is a refresh button. A click on it also refreshes the list data. The topics we will discuss in this tutorial include: + - [Setting up a reactive state, selections, and UI interactions][1-setup] - [Handling @Inputs reactively][2-input-bindings] - [Handling @Output reactively][3-output-bindings] diff --git a/apps/docs/docs/cdk/_category_.json b/apps/docs/docs/cdk/_category_.json index ec7cadc82b..111d9f8584 100644 --- a/apps/docs/docs/cdk/_category_.json +++ b/apps/docs/docs/cdk/_category_.json @@ -1,8 +1,3 @@ { - "label": "@rx-angular/cdk", - "position": 1, - "link": { - "type": "generated-index", - "description": "RxAngular CDK" - } + "label": "@rx-angular/cdk" } diff --git a/apps/docs/docs/cdk/api/_category_.json b/apps/docs/docs/cdk/api/_category_.json index 8ec0189c3a..b53f31d81a 100644 --- a/apps/docs/docs/cdk/api/_category_.json +++ b/apps/docs/docs/cdk/api/_category_.json @@ -1,4 +1,9 @@ { "label": "API", - "link": null + "position": 100, + "link": { + "type": "generated-index", + "title": "API reference", + "slug": "/state/cdk" + } } diff --git a/apps/docs/docs/cdk/cdk.mdx b/apps/docs/docs/cdk/cdk.mdx new file mode 100644 index 0000000000..1e9e8cb85b --- /dev/null +++ b/apps/docs/docs/cdk/cdk.mdx @@ -0,0 +1,15 @@ +--- +sidebar_label: '@rx-angular/cdk' +sidebar_position: 1 +title: 'CDK' +hide_title: true +--- + +import Readme, { toc as readmeToc } from '@site/../../libs/cdk/README.md'; + + + + + + +export const toc = [...readmeToc]; diff --git a/libs/cdk/coalescing/docs/Readme.md b/apps/docs/docs/cdk/coalescing/_docs.md similarity index 80% rename from libs/cdk/coalescing/docs/Readme.md rename to apps/docs/docs/cdk/coalescing/_docs.md index 0f8153ef46..34bf1ca6de 100644 --- a/libs/cdk/coalescing/docs/Readme.md +++ b/apps/docs/docs/cdk/coalescing/_docs.md @@ -1,13 +1,13 @@ -# Resources +## Resources -**Example applications:** +**Example applications:** A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angular-cdk-coalescing). -# Motivation +## Motivation Coalescing means multiple things "merge" into one. -![RxAngular - CDK/Coalescing](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing.png) +![RxAngular - CDK/Coalescing](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing.png) If two or more things coalesce, they come merge togeather into one thing or system. Natively Angular is using this under the hood already for a long time. @@ -16,13 +16,13 @@ In RxAngular coalescing is used for merging multiple emissions, streams or calls The next example shows the effect of coalescing visualized in flame charts. -![coalesceWith - micro taks duration selector](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_duration-selector-micro-task.png) +![coalesceWith - micro taks duration selector](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_duration-selector-micro-task.png) _no coalescing vs. coalescing on microtask. visualized in flame charts_ The non-coalesced component has three consequetive heavy computations in the template whilest the coalesced component only has to do the same computation once in order to complete the same job. Even on a small components scale the difference in performance can be significant. -# Available Approaches +## Available approaches There are 2 places in Angular we have coalescing already implemented in the framework: @@ -31,6 +31,7 @@ There are 2 places in Angular we have coalescing already implemented in the fram - RxAngular adds another option where we can apply those techniques manually wherever we want. **The Benefits** + - ✅ Coalescing techniques as RxJS operators - ✅ Configurable durationSelector for all kind of scheduling methods - ✅ Scope coalescing to a specific component or object @@ -40,7 +41,7 @@ There are 2 places in Angular we have coalescing already implemented in the fram Before we dive into the usage of this package we may want to understand already existing coalescing mechanisms in Angular and why it is used to get better performance. -## Coalescing of `ApplicationRef#tick` calls +### Coalescing of `ApplicationRef#tick` calls As chances are high multiple changes occur at the same time Angular's change detection would end up getting triggered also multiple times. This is the reason why Angular implemented coalescing for those calls. @@ -49,16 +50,16 @@ In the following image we see 3 changes that call `ChangeDetectorRef#markForChec The internal logic then delays these calls for a while by using `requestAnimationFrame` and calls `ApplicationRef#tick` only one time after the next animation frame arrives. This way Angular's change detection and re-evaluation/re-rendering of the app get executed only once for all calls that fall into the duration from invocation until the next animation frame lands. -![Angular - Coalescing re-render caused by `markForCheck` diagram](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__appRef-tick-coalescing.png) +![Angular - Coalescing re-render caused by `markForCheck` diagram](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__appRef-tick-coalescing.png) If we visualize the same behavior based on flame charts, we can understand the internal logic and naming of the different steps in that process more technically. The graphic shows the start of the coalescing duration, the different browser events and where the execution of certain logic is moved to. -![Angular - Coalescing re-render caused by `markForCheck` flame charts](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__appRef-tick-coalescing-flames.png) +![Angular - Coalescing re-render caused by `markForCheck` flame charts](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__appRef-tick-coalescing-flames.png) With that information, we should be able to reflect this concept also onto other usages in Angular. -## Coalescing with `ngZoneEventCoalescing` settings +### Coalescing with `ngZoneEventCoalescing` settings Angular's bootstrap method can be configured to use a config property called `ngZoneEventCoalescing`. @@ -68,12 +69,12 @@ platformBrowserDynamic() .catch((err) => console.error(err)); ``` -This setting applies the technique of coalescing to fired events bound by Angular. +This setting applies the technique of coalescing to fired events bound by Angular. It will coalesce any event emissions occurring during the duration of an animation frame and after that, run `ApplicationRef#tick` only one time instead of multiple times. This is mainly impactful if we deal with event-heavy templates. The diagrams below shows the difference between 2 events with and without coalescing. -![Angular - ngZoneEventCoalescing Cefore](https://user-images.githubusercontent.com/10064416/122643339-92a60300-d10f-11eb-9e6c-0ebd3dbe7c45.png) +![Angular - ngZoneEventCoalescing Before](https://user-images.githubusercontent.com/10064416/122643339-92a60300-d10f-11eb-9e6c-0ebd3dbe7c45.png) ![Angular - ngZoneEventCoalescing After](https://user-images.githubusercontent.com/10064416/122643340-946fc680-d10f-11eb-952d-9f19d6245d2d.png) ![Angular - ngZoneEventCoalescing Details](https://user-images.githubusercontent.com/10064416/122643341-96398a00-d10f-11eb-8815-9ed7ec00ac11.png) @@ -101,7 +102,7 @@ As these situations typically occur across multiple components or are hard to sc ``` -# RxAngular Coalescing operators +## RxAngular coalescing operators While developing RxAngular, one of the first things we had to tackle for performant change detection was coalescing of `ChangeDetectorRef#detectChanges` calls on component level, but in fact, the shipped logic can be applied anywhere. @@ -113,13 +114,13 @@ There are 2 main pieces to understand: In the section usage we will go into more detail. -## Marble Diagram +### Marble Diagram -![Angular - coalesceWith shifted version](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_coalesceWith1.png) +![Angular - coalesceWith shifted version](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_coalesceWith1.png) -![Angular - coalesceWith aligned version](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_coalesceWith2.png) +![Angular - coalesceWith aligned version](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_coalesceWith2.png) -## Setup +### Setup The coalescing features can be used directly from the `cdk` package or indirectly through the `template` package. To do so, install the `cdk` package and, if needed, the packages depending on it: @@ -132,7 +133,7 @@ npm i @rx-angular/cdk yarn add @rx-angular/cdk ``` -## Usage +### Usage As coalescing was already explained in the Angular context, we can take that one step further and look at it in a more agnostic way. @@ -156,19 +157,19 @@ RxAngular's `coalesceWith` operator helps to merge together when applied to the from([1, 2, 3]).pipe(coalesceWith()).subscribe(doStuff); // 1 x doStuff logs 3 ``` -### Coalescing duration +#### Coalescing duration By default, the duration in which values get united is derived from a micro task which executes immediately after the synchronous code got executed. See the diagram for details: -![coalesceWith - macro taks duration selector](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_duration-selector-micro-task-flames.png) +![coalesceWith - macro taks duration selector](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_duration-selector-micro-task-flames.png) To have more fine-grained control over the duration of coalescing an optional parameter `durationSelector` is provided. `durationSelector` is of type `Observable` and the first emission of it terminates the coalescing duration. You could pass e.g. `interval(0)` as `durationSelector` to use a `setInterval` as duration period. -> **💡 Pro Tip** +> **💡 Pro Tip** > Even a longer duration based on milliseconds, e.g. `interval(500)` can be used as duration. > > For more information on the different scheduling options you could have a look at the different scheduling API's like @@ -177,9 +178,9 @@ You could pass e.g. `interval(0)` as `durationSelector` to use a `setInterval` a A real life example where `coalesceWith` comes in handy is runnning manual change detection with `ChangeDetectorRef#detectChanges()`. The below diagram displays the cycle of updates, coalescing and rendering of values in a component. -![coalesceWith - one component](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__coalesceWith-on-component.png) +![coalesceWith - one component](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__coalesceWith-on-component.png) -### Coalescing scope +#### Coalescing scope If we think about the underlying principle of coalescing a little bit more we may ask our self how the logic knows what to do? How is it done that some work that is scheduled multiple times get executed only once? Surely there must be a variable stored somewhere that knows if coalescing is currently ongoing or not. @@ -222,12 +223,12 @@ With this in mind, we can go one step further and look at change detection acros The following diagram illustrates change detection in component level: -![coalesceWith - multiple components with component scope](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__coalesceWith-on-component-component-scope.png) +![coalesceWith - multiple components with component scope](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__coalesceWith-on-component-component-scope.png) -> **⚠ Notice:** +> **⚠ Notice:** > Be cautious with globally shared coalescing scopes. It could lead to unwanted behaviour and loss of updates when used incorrectly. -![coalesceWith - multiple components with global scope](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__coalesceWith-on-component-global-scope.png) +![coalesceWith - multiple components with global scope](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing__coalesceWith-on-component-global-scope.png) Again, why is this the case? @@ -246,11 +247,11 @@ from([component1, component2, component3]) .subscribe((component) => component.cdr.detectChanges()); // only component 3 gets called ``` -# Example usage in RxAngular +## Example usage in RxAngular As RxAngular/cdk packages are not only here to build other tools but also to build RxAngular it self lets see where we used it under the hood. -In the [template](https://github.com/rx-angular/rx-angular/edit/main/libs/template) package we have a couple of directives and pipes that use the coalescing logic internally. +In the [template](https://rx-angular.io/docs/template) package we have a couple of directives and pipes that use the coalescing logic internally. It is done in a way where the directives and services automatically take the most performant scope to bind coalescing to. The example below shows multiple components rendering the same or parts of the same value. The scopes are applied automatically and named for all different usages. @@ -261,4 +262,4 @@ The example below shows multiple components rendering the same or parts of the s - As pipe in the component's template - As structural directive in the component's template -![Coalescing Scope Example](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_coalescing-scope-example.png) +![Coalescing Scope Example](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/coalescing/docs/images/rx-angular-cdk-coalescing_coalescing-scope-example.png) diff --git a/apps/docs/docs/cdk/coalescing/coalescing.mdx b/apps/docs/docs/cdk/coalescing/coalescing.mdx new file mode 100644 index 0000000000..ebe7720003 --- /dev/null +++ b/apps/docs/docs/cdk/coalescing/coalescing.mdx @@ -0,0 +1,20 @@ +--- +sidebar_label: 'Coalescing' +sidebar_position: 1 +title: 'Coalescing' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/cdk/coalescing/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/libs/cdk/coercing/docs/Readme.md b/apps/docs/docs/cdk/coercing/_docs.md similarity index 94% rename from libs/cdk/coercing/docs/Readme.md rename to apps/docs/docs/cdk/coercing/_docs.md index e800bf8331..497cad20ef 100644 --- a/libs/cdk/coercing/docs/Readme.md +++ b/apps/docs/docs/cdk/coercing/_docs.md @@ -1,18 +1,17 @@ -# Resources +## Resources -**Example applications:** +**Example applications:** A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angular-cdk-coercing). -# Motivation +## Motivation Coercing, or to be more specific type coercion is the process of converting a value from one type to another. -This can be done with any primitive value in JavaScript. e.g. number, string, Symbol, boolean, null, undefined. +This can be done with any primitive value in JavaScript. e.g. number, string, Symbol, boolean, null, undefined. Another type where we also can apply coercing is the `Observable` type. ![coerceObservable](https://user-images.githubusercontent.com/10064416/129430812-1e4af911-fb42-4d61-a68a-4fb86e595113.png) - In practice you can apply this technique in 2 ways: - **explicitly** e.g. `Number(string)` coercres a string to a number @@ -54,10 +53,10 @@ export class AppComponent { } ``` -# Available Approaches +## Available approaches - [@angular/cdk/coercion](https://www.npmjs.com/package/@angular/cdk) -- [@rx-angular/cdk/coercion](https://www.npmjs.com/package/@rx-angular/cdk) +- [@rx-angular/cdk/coercing](https://www.npmjs.com/package/@rx-angular/cdk) **The Benefits of RxAngular** @@ -67,7 +66,7 @@ export class AppComponent { - ✅ Fully tested - ✅ Well Documented -# RxAngular Coercing helpers +## RxAngular coercing helpers **Factories:** @@ -82,7 +81,7 @@ export class AppComponent { In the section usage we will go into more detail. -## Setup +### Setup The coalescing features can be used directly from the `cdk` package or indirectly through the `template` package. To do so, install the `cdk` package and, if needed, the packages depending on it: @@ -95,7 +94,7 @@ npm i @rx-angular/cdk yarn add @rx-angular/cdk ``` -## Usage +### Usage In the following we will sketch some usecases where coercing operators can be used. @@ -163,7 +162,9 @@ This comes in handy for later processing and improves performance. }) export class AppComponent { _prop1 = new Subject>(); - prop1Observables$: Observable = this._prop1.pipe(coerceDistinctWith()); + prop1Observables$: Observable = this._prop1.pipe( + coerceDistinctWith() + ); @Input() set prop1(val: Observable | number) { @@ -199,8 +200,8 @@ A minimal implementation if it would look like this: export class AppComponent { _prop1 = new Subject>(); prop1Changes$: Observable = this._prop1.pipe( - coerceDistinctWith(), - switchAll() + coerceDistinctWith(), + switchAll() ); @Input() @@ -215,7 +216,6 @@ Here we apply the `switchAll` operator to unsubscribe the previouse observable a by using the `coerceAllFactory` function we can compose this pattern with less code and still have the option to decide on the way of compostion. - ```typescript @Component({ // ... @@ -223,7 +223,7 @@ by using the `coerceAllFactory` function we can compose this pattern with less c export class AppComponent { _prop1 = coerceAllFactory(); prop1Changes$: Observable = this._prop1.values$; - + @Input() set prop1(val: Observable | number) { this._prop1.next(val); @@ -233,7 +233,7 @@ export class AppComponent { Another benefit here is that only the `next` method is exposed. -By default a normal `Subject` is used and `switchAll` is applied. +By default a normal `Subject` is used and `switchAll` is applied. As there are quite some ways to process higher order observables the factory provides optional configuration parameter. ```typescript diff --git a/apps/docs/docs/cdk/coercing/coercing.mdx b/apps/docs/docs/cdk/coercing/coercing.mdx new file mode 100644 index 0000000000..ed5012070a --- /dev/null +++ b/apps/docs/docs/cdk/coercing/coercing.mdx @@ -0,0 +1,20 @@ +--- +sidebar_label: 'Coercing' +sidebar_position: 1 +title: 'Coercing' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/cdk/coercing/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/libs/cdk/notifications/docs/Readme.md b/apps/docs/docs/cdk/notifications/_docs.md similarity index 62% rename from libs/cdk/notifications/docs/Readme.md rename to apps/docs/docs/cdk/notifications/_docs.md index aa58f1d578..0ecf842231 100644 --- a/libs/cdk/notifications/docs/Readme.md +++ b/apps/docs/docs/cdk/notifications/_docs.md @@ -1,9 +1,9 @@ -# Resources +## Resources -**Example applications:** +**Example applications:** A demo application is available on [GitHub](https://stackblitz.com/edit/angular-async-ngif-with-error-tpb4uc). -# Motivation +## Motivation ![rx-angular_cdk_notifications__toNotifiaction_michael_hladky](https://user-images.githubusercontent.com/10064416/131154927-c56e1fbe-c01f-4c8a-9445-869db3f9bd0e.png) @@ -12,50 +12,45 @@ When dealing with asynchronouse code we always have some contextual information The a good example is a `[Promise](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Promise)` used in a UI where you can list items and search them. The following states can apply to this UI: + - Initial loading of the list. (loading spinner) - Display of the data (The actual value is now given and displayed) - Error in the asynchronouse process (A error message is displayed) - Completion of the process (Communicates that the process is completed) - Subsquent search actions (loading spinner) - ```typescript @Component({ selector: 'any-component', template: ` - +

Count: {{ count }}

- - Negative Count - + Negative Count
-

- Error! -

+

Error!

-

- Complete! -

+

Complete!

-

- Loading... -

+

Loading...

- ` + `, }) -export class AnyComponent { +export class AnyComponent { // ... } ``` If we organize them visually 4 states, 3 of them contextual are given: + - **suspense** (communicating progress) - update/**next** (the result it self, or parts of it) - **error** (communicating error) @@ -67,39 +62,39 @@ For those states we use the term **reactive context** which includes the state a Whith this concept we can create helpers that support to implement the handling of reactive context in a more elegant way. -A good example is the [`rxLet`](https://github.com/rx-angular/rx-angular/blob/main/libs/template/docs/api/let-directive.md) directive: +A good example is the [`rxLet`](../../template/api/let-directive.md) directive: ```typescript @Component({ selector: 'any-component', template: ` -

+

Count: {{ count }}

- - Negative Count - - - - Error! - + Negative Count - - Complete! - + Error! - - Loading... - - ` + Complete! + + Loading... + `, }) -export class AnyComponent { +export class AnyComponent { // ... } ``` - **The Benefits** - ✅ A mental model for contextual state @@ -122,7 +117,7 @@ yarn add @rx-angular/cdk ## Usage -The whole section is about extending the notification channels with a 4th state. +The whole section is about extending the notification channels with a 4th state. The new type is called `RxNotifications`. In the following we will see a couple of helper functions that deal with that type. For wrapping a value into a RxNotification we provide 3 helpers: @@ -130,30 +125,39 @@ For wrapping a value into a RxNotification we provide 3 helpers: **RxErrorNotification** ```typescript - const errorNotification: RxErrorNotification = toRxErrorNotification(); - const errorNotification: RxErrorNotification = toRxErrorNotification(new Error()); - const errorNotification: RxErrorNotification = toRxErrorNotification(new Error(), 'lastValue'); +const errorNotification: RxErrorNotification = toRxErrorNotification(); +const errorNotification: RxErrorNotification = toRxErrorNotification( + new Error() +); +const errorNotification: RxErrorNotification = toRxErrorNotification( + new Error(), + 'lastValue' +); ``` **toRxSuspenseNotification** ```typescript - const toRxSuspenseNotification: RxSuspenseNotification = toRxSuspenseNotification(); - const toRxSuspenseNotification: RxSuspenseNotification = toRxSuspenseNotification('lastValue'); +const toRxSuspenseNotification: RxSuspenseNotification = + toRxSuspenseNotification(); +const toRxSuspenseNotification: RxSuspenseNotification = + toRxSuspenseNotification('lastValue'); ``` - + **toRxCompleteNotification** ```typescript - const toRxCompleteNotification: RxCompleteNotification = toRxCompleteNotification(); - const toRxCompleteNotification: RxCompleteNotification = toRxCompleteNotification('lastValue'); +const toRxCompleteNotification: RxCompleteNotification = + toRxCompleteNotification(); +const toRxCompleteNotification: RxCompleteNotification = + toRxCompleteNotification('lastValue'); ``` **rxMaterialize** ```typescript - const websocketUpdates$: Observable = interval(3000); - const materialized$: Observable> = websocketUpdates.pipe( - rxMaterialize() - ); +const websocketUpdates$: Observable = interval(3000); +const materialized$: Observable> = websocketUpdates.pipe( + rxMaterialize() +); ``` diff --git a/apps/docs/docs/cdk/notifications/notifications.mdx b/apps/docs/docs/cdk/notifications/notifications.mdx new file mode 100644 index 0000000000..842ddbdf2e --- /dev/null +++ b/apps/docs/docs/cdk/notifications/notifications.mdx @@ -0,0 +1,20 @@ +--- +sidebar_label: 'Notifications' +sidebar_position: 1 +title: 'Notifications' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/cdk/notifications/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/libs/cdk/render-strategies/docs/README.md b/apps/docs/docs/cdk/render-strategies/_docs.md similarity index 90% rename from libs/cdk/render-strategies/docs/README.md rename to apps/docs/docs/cdk/render-strategies/_docs.md index 6ad9e79910..1d1399aaea 100644 --- a/libs/cdk/render-strategies/docs/README.md +++ b/apps/docs/docs/cdk/render-strategies/_docs.md @@ -1,6 +1,6 @@ -# Render Strategies +## Render strategies -##### Explicit fine-grained control of change detection in Angular +> Explicit fine-grained control of change detection in Angular ![RenderStrategies - Overview](https://user-images.githubusercontent.com/10064416/116306287-2b21a600-a7a5-11eb-997b-5c2e847821a2.png) @@ -15,7 +15,7 @@ Furthermore, they provide new ways of explicitly tying truly push-based state ma A strategy exposes the work to perform (e.g. `cdRef#markForCheck`, `cdRef#detectChanges`) as well as the scheduling mechanism to developers for configuration & customization via the interface `RxStrategyCredentials`. -`Directive`s, `Service`s or `Component`s of your application can make use of these strategies as an easy API for the key [concepts](https://github.com/rx-angular/rx-angular/blob/main/libs/template/docs/concepts.md) of rendering performance. +`Directive`s, `Service`s or `Component`s of your application can make use of these strategies as an easy API for the key [concepts](../../template/concepts/concepts.md) of rendering performance. This architecture enables modern features like: @@ -31,7 +31,7 @@ This architecture enables modern features like: **BasicStrategies** -[BasicStrategies](https://github.com/rx-angular/rx-angular/blob/master/libs/cdk/render-strategies/docs/basic-strategies.md) wrap modern ivy APIs like `ɵmarkDirty` and `ɵdetectChanges` as well as a strategy to "noop" change detection. +[BasicStrategies](strategies/basic-strategies.md) wrap modern ivy APIs like `ɵmarkDirty` and `ɵdetectChanges` as well as a strategy to "noop" change detection. As a fallback for the migration process or comparison testing, Angulars default change detection behaviour is also provided as a strategy. This set aims to get the first option for zone-less rendering (`ɵmarkDirty`), more control on the top-down process, and improve performance drastically by only rendering components that received updates. @@ -40,7 +40,7 @@ This set aims to get the first option for zone-less rendering (`ɵmarkDirty`), m **ConcurrentStrategies** -The [ConcurrentStrategies](https://github.com/rx-angular/rx-angular/blob/master/libs/cdk/render-strategies/docs/concurrent-strategies.md) utilize the latest technologies to enable priority-based change detection for non-blocking rendering and smooth user experiences. It combines the most performant scheduling techniques with a highly performant queueing mechanism. +The [ConcurrentStrategies](strategies/concurrent-strategies.md) utilize the latest technologies to enable priority-based change detection for non-blocking rendering and smooth user experiences. It combines the most performant scheduling techniques with a highly performant queueing mechanism. Read more about the internal techniques [here](https://www.npmjs.com/package/scheduler) or [here](https://github.com/WICG/scheduling-apis). The name **ConcurrentStrategies** implies that concepts of [react concurrent mode](https://reactjs.org/docs/concurrent-mode-intro.html) are transported into the world of Angular. @@ -53,7 +53,6 @@ With these sets of strategies and the possibility of switching them at runtime w ![concurrent scheduling - abstract diagram](https://user-images.githubusercontent.com/10064416/145224962-04147632-f634-4025-a097-8135cdf9f3cc.png) - **Render strategies pave the way for truly non-blocking applications, targeted for any device or platform 🚀** + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/libs/cdk/render-strategies/docs/strategy-provider.md b/apps/docs/docs/cdk/render-strategies/rx-strategy-provider.md similarity index 84% rename from libs/cdk/render-strategies/docs/strategy-provider.md rename to apps/docs/docs/cdk/render-strategies/rx-strategy-provider.md index 155dd842cf..f7a22cfd11 100644 --- a/libs/cdk/render-strategies/docs/strategy-provider.md +++ b/apps/docs/docs/cdk/render-strategies/rx-strategy-provider.md @@ -2,17 +2,17 @@ `RxStrategyProvider` is the best way to consume full power of concurrent strategies to schedule any kind of work. -> Want to play with it? Here's [demo link](https://stackblitz.com/edit/angular-ivy-1vfpoe) +> Want to play with it? Here's [demo link](https://stackblitz.com/edit/angular-ivy-1vfpoe) ## Motivation -Chromium based browsers considers all tasks that taking more than 50ms as long tasks. If task runs more than 50ms, users will start noticing lags. Optimally all user interactions should happen at 30 fps framerate with 32ms budget per browser task. In ideal world it should be 60 fps and 16ms budget. +Chromium based browsers considers all tasks that taking more than 50ms as long tasks. If task runs more than 50ms, users will start noticing lags. Optimally all user interactions should happen at 30 fps framerate with 32ms budget per browser task. In ideal world it should be 60 fps and 16ms budget. > 💡 In reality browser has a reserved overhead of 4ms, try to stick to 28ms of work for 30 fps and 12ms for 60 fps. ## Scheduling mechanisms in browser -There are multiple ways to schedule task in the browser. +There are multiple ways to schedule task in the browser. - `setTimeout` - `requestAnimationFrame` @@ -26,9 +26,9 @@ None of them has a full notion about what happens in the browser and can be unre > 💡 Under the hood all our concurrent strategies are based on MessageChannel technology. -To address the problem of long tasks and help browser split the work @rx-angular/cdk provides concurrent strategies. This strategies will help browser to chunk the work into non-blocking tasks whenever it's possible. +To address the problem of long tasks and help browser split the work @rx-angular/cdk provides concurrent strategies. This strategies will help browser to chunk the work into non-blocking tasks whenever it's possible. -You can read detailed information about concurrent strategies [here](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/render-strategies/docs/concurrent-strategies.md). +You can read detailed information about concurrent strategies [here](strategies/concurrent-strategies.md). ## RxStrategyProvider APIs @@ -37,7 +37,7 @@ Full signature of the service available below, but we will focus only on methods ```typescript @Injectable({ providedIn: 'root' }) export class RxStrategyProvider { - + get config(): Required>; get strategies(): RxStrategies; @@ -78,6 +78,7 @@ export class RxStrategyProvider { This method returns current `RxAngularConfig` used in the service. Config includes: + - strategy that currently in use - `primaryStrategy` - array of custom user defined strategies - `customStrategies` - setting that is responsible for running in our outside of the zone.js - `patchZone` @@ -85,7 +86,7 @@ Config includes: #### Usage example ```typescript -const defaultConfig = this.strategyProvider.config; +const defaultConfig = this.strategyProvider.config; ``` ### `get strategies()` @@ -119,7 +120,7 @@ const primaryStrategy = this.strategyProvider.primaryStrategy; ``` ```html -
+
``` ### `set primaryStrategy()` @@ -143,7 +144,7 @@ const primaryStrategy$ = this.strategyProvider.primaryStrategy$; ``` ```html -
+
``` ### `strategies$` @@ -158,7 +159,7 @@ const strategyNames$ = this.strategyProvider.strategyNames$; ### `schedule` & `scheduleWith` -These methods allows users to schedule any kind of work. +These methods allows users to schedule any kind of work. - `schedule` returns an observable (don't forget to subscribe!) - `scheduleWith` returns a `MonoTypeOperatorFunction` so you can use this method inside rxjs `pipe` @@ -176,16 +177,19 @@ Options are configuration object that you can use to setup the scheduling behavi #### Usage examples ```typescript -this.strategyProvider.schedule(() => - myWork(), - {strategy: 'idle', patchZone: false, scope: this} -).subscribe(); - -myObservable$.pipe( - this.strategyProvider.scheduleWith( - () => myWork(), - {strategy: 'idle', patchZone: false, scope: this}) -).subscribe(); +this.strategyProvider + .schedule(() => myWork(), { strategy: 'idle', patchZone: false, scope: this }) + .subscribe(); + +myObservable$ + .pipe( + this.strategyProvider.scheduleWith(() => myWork(), { + strategy: 'idle', + patchZone: false, + scope: this, + }) + ) + .subscribe(); ``` ### `scheduleCD` method @@ -198,10 +202,11 @@ Imperative method that you can use to schedule change detection cycle. You must #### Usage example ```typescript -this.strategyProvider.scheduleCd(this.changeDetectorRef, {afterCD: myWork()}); +this.strategyProvider.scheduleCd(this.changeDetectorRef, { afterCD: myWork() }); ``` ## Links + - [Demo](https://stackblitz.com/edit/angular-ivy-1vfpoe) -- [Detailed information about strategies](https://github.com/rx-angular/rx-angular/tree/master/libs/cdk/render-strategies) +- [Detailed information about strategies](render-strategies.mdx) - [MessageChannel documentation](https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel) diff --git a/libs/cdk/render-strategies/docs/basic-strategies.md b/apps/docs/docs/cdk/render-strategies/strategies/basic-strategies.md similarity index 86% rename from libs/cdk/render-strategies/docs/basic-strategies.md rename to apps/docs/docs/cdk/render-strategies/strategies/basic-strategies.md index 0111bd27a8..3d14ed5db6 100644 --- a/libs/cdk/render-strategies/docs/basic-strategies.md +++ b/apps/docs/docs/cdk/render-strategies/strategies/basic-strategies.md @@ -1,4 +1,4 @@ -# Basic Strategies +# Basic strategies ## Concepts @@ -8,6 +8,7 @@ To apply changes to a component template we need to re-evaluate the template. In This process will execute whenever a component's template is re-evaluated through `async`, `push`, `ChangedDetectorRef.detectChanges` or a structural directive template is re-evaluated through `EmbeddedView.detectChanges`. It can be pretty time consuming and directly depends on the following factors: + - HTML size (only for init and destroy and bundle-size) - JS size (only for init and destroy and bundle-size) - number of event bindings (only for init and destroy) @@ -20,27 +21,29 @@ Some of the problems related to work of Angular are: **Out of bound change detection:** If we perform this re-evaluation without any visual change for the user (over-rendering) we introduce noticeable performance degradations. -The out of bound change detection can be caused through: - - Zone pollution - - Missing ChangeDetectionStrategy.OnPush - - Component template projection - - Pull-based rendering processes - +The out of bound change detection can be caused through: + +- Zone pollution +- Missing ChangeDetectionStrategy.OnPush +- Component template projection +- Pull-based rendering processes + **Out of bound template evaluation:** -If we perform a re-evaluation of a single property in the template any other expression/binding also gets re-evaluated. +If we perform a re-evaluation of a single property in the template any other expression/binding also gets re-evaluated. Again over-rendering introduces noticeable performance degradations. The out of bound template evaluation can be caused through: - - Pull-based rendering processes - - any reactive change through `async` - - any call to `cdRef.detectChanges` +- Pull-based rendering processes +- any reactive change through `async` +- any call to `cdRef.detectChanges` **Work performed for out of viewport content:** If we perform a re-evaluation or even re-rendering of the DOM of elements outside of the viewport we perform useless work for the user. This will pollute the main thread and reduced time for more important content to get rendered. The re-evaluation or browser re-rendering can be caused by: + - Bad style changes - Big LCP (Largest Contentful Paint) elements - Large amount of content @@ -50,7 +53,7 @@ The re-evaluation or browser re-rendering can be caused by: ![ChangeDetection](https://user-images.githubusercontent.com/10064416/143149592-9a55eafc-3b44-412c-a146-acb777a2e777.png) The change detection system that is currently implemented in Angular is pull-based, but way more important, as a side effect it also runs CD globally. -It performs a re-rendering where at optimum every single component on the path from the root to the actual UI update needs to get re-evaluated. +It performs a re-rendering where at optimum every single component on the path from the root to the actual UI update needs to get re-evaluated. A lot of performed work is useless. Technically the methods to run change detection are `markForCheck` / `markViewDirty`, `ɵmarkDirty` and `tick`. @@ -61,32 +64,30 @@ Technically the methods we can use for it are `detectChanges` or `ɵdetectChange ![Render Strategies-global-vs-local](https://user-images.githubusercontent.com/10064416/143150010-fa01316a-acd9-4906-ab81-25a29336cf57.png) - ### Pull vs push based - ![Render Strategies-requext-subscribe](https://user-images.githubusercontent.com/10064416/143153116-782bec55-0353-4254-8fe5-5a16691ac320.png) Consuming value changes can be done by **constantly** watching the source for changes and **pull** them, - or subscribe to the changes like a DOM event binding **once** and get the changes **pushed**. +or subscribe to the changes like a DOM event binding **once** and get the changes **pushed**. In a simple setup the pull might be a quick solution and you just `.get()` the value, but a push based architecture always scales better. Compare it with HTTP calls vs WebSockets. -If we apply this concepts to our change detection mechanics we can directly apply changes where they are needd and skip nearly all the unnessecary work. +If we apply this concepts to our change detection mechanics we can directly apply changes where they are needd and skip nearly all the unnessecary work. In combination with Observables, and EmbeddedViews change detection can be speed up dramatically by this architecture. - + ![Render Strategies-pull-vs-push](https://user-images.githubusercontent.com/10064416/143150014-e83347e4-188c-447d-8d61-2fc3014f5abb.png) ### Strategies -| Name | Priority | Render Method | Scheduling | Render Deadline | -|-------------------------| -------- | ----------------- | ----------------------- | --------------- | -| `"native"` | ❌ | ⮁ `markForCheck` | `requestAnimationFrame` | N/A | +| Name | Priority | Render Method | Scheduling | Render Deadline | +| ------------------------- | -------- | ----------------- | ----------------------- | --------------- | +| `"native"` | ❌ | ⮁ `markForCheck` | `requestAnimationFrame` | N/A | | `"global"` - _deprecated_ | ❌ | ⮁ `ɵmarkDirty` | `requestAnimationFrame` | N/A | -| `"local"` | ❌ | 🠗 `detectChanges` | `requestAnimationFrame` | N/A | -| `"noop"` | ❌ | - `noop` | `requestAnimationFrame` | N/A | +| `"local"` | ❌ | 🠗 `detectChanges` | `requestAnimationFrame` | N/A | +| `"noop"` | ❌ | - `noop` | `requestAnimationFrame` | N/A | #### Native @@ -103,7 +104,7 @@ as the internally called function [`markViewDirty`](https://github.com/angular/a #### Global Strategy -> **deprecated** +> **deprecated** > angular [drops support](https://github.com/angular/angular/pull/46806) for `ɵmarkDirty` This strategy leverages Angular's internal [`ɵmarkDirty`](https://github.com/angular/angular/blob/930eeaf177a4c277f437f42314605ff8dc56fc82/packages/core/src/render3/instructions/change_detection.ts#L36) render method. @@ -155,41 +156,32 @@ The no-operation strategy does nothing. It can be a valuable tool for performanc | ------ | ------------- | ------------- | ---------- | ---------- | | `noop` | ✔ | - `noop` | ❌ | ❌ | - ## Usage ### Component / Service ```ts -import {RxStrategyProvider} from '@rx-angular/cdk/render-strategies'; +import { RxStrategyProvider } from '@rx-angular/cdk/render-strategies'; @Component() class Component { - constructor(private strategyProvider: RxStrategyProvider) { - strategyProvider.schedule(() => {}, {strategyName: 'local'}) + strategyProvider.schedule(() => {}, { strategyName: 'local' }); } - } ``` ### Template ```ts -import {LetModule} from '@rx-angular/template/let'; -import {ForModule} from '@rx-angular/template/for'; -import {PushModule} from '@rx-angular/template/push'; +import { LetModule } from '@rx-angular/template/let'; +import { ForModule } from '@rx-angular/template/for'; +import { PushModule } from '@rx-angular/template/push'; @Module({ - imports: [ - LetModule, - ForModule, - PushModule - ] + imports: [LetModule, ForModule, PushModule], }) -class Module { - -} +class Module {} ``` ```html diff --git a/libs/cdk/render-strategies/docs/concurrent-strategies.md b/apps/docs/docs/cdk/render-strategies/strategies/concurrent-strategies.md similarity index 90% rename from libs/cdk/render-strategies/docs/concurrent-strategies.md rename to apps/docs/docs/cdk/render-strategies/strategies/concurrent-strategies.md index 10e818dcbf..297fdacc44 100644 --- a/libs/cdk/render-strategies/docs/concurrent-strategies.md +++ b/apps/docs/docs/cdk/render-strategies/strategies/concurrent-strategies.md @@ -1,17 +1,19 @@ -# Concurrent Strategies +# Concurrent strategies Based on the [RAIL model](https://web.dev/rail/), e.g. if your app provides animated user feedback within more than 16ms (less than 60 frames per second), it feels laggy to the user and leads to bad UX. From the UX perspective that means users should not experience blocking periods more than 16 ms. ## Concepts + There are 5 core concepts of the concurrent strategies: + - Frame budget / Frame drop - Scheduling - Priority - Chunking - Concurrent Scheduling -### Frame budget / Frame Drop +### Frame budget / Frame drop The Browser has only one UI thread (main thread), meaning things happen one after another. @@ -48,11 +50,12 @@ When it comes to scripting work we can do 2 things to avoid that: - reduce scripting work and let the user interact earlier - chunk up work and use scheduling API's to distribute the work overtime and let the user interact in between. -It is often the case that the work just can't get reduced so we have to schedule. +It is often the case that the work just can't get reduced so we have to schedule. ![Render Strategies-Scheduling Detail View](https://user-images.githubusercontent.com/10064416/145210963-be6d2dc0-f4e3-4654-9f7f-f5221976ed0c.png) Some of the possible APIs are: + - queueMicrotask - setTimeout - postMessage @@ -70,7 +73,7 @@ A simple way to schedule work is using `setTimeout`. ```typescript function work(): viod { - concole.log('work done!'); + concole.log('work done!'); } const asyncId = setTimeout(work); @@ -84,11 +87,10 @@ This is important for cancellation and cleanup logic. ```typescript clearTimeout(asyncId); -``` +``` If we pass the asyncId as parameter to the `clearTimeout` function we can cancel the scheduling and `work` will never get executed. - ### Priority ![Render Strategies - priority abstract diagram png](https://user-images.githubusercontent.com/10064416/145228352-da3487fa-41c0-497b-a49a-c274bf531d32.png) @@ -102,7 +104,7 @@ Input handlers (tap, click etc.) often need to schedule a combination of differe To get the best user experience we should prioritize this tasks. -There are couple of scheduling APIs mentioned under scheduling. +There are couple of scheduling APIs mentioned under scheduling. They all help to prioritize the work and define the moment of execution differently. ![Render Strategies - scheduling techniques](https://user-images.githubusercontent.com/10064416/144139079-9f1d6ad7-ad7e-437c-95a2-8a794460f9c9.png) @@ -127,14 +129,15 @@ This scenario gets to a problem depending on: - the number of Angular elements - the amount of work done in the elements -### Concurrent Scheduling +### Concurrent scheduling ![concurrent scheduling - abstract diagram](https://user-images.githubusercontent.com/10064416/145228577-6b8f0bb7-6547-4835-aecc-13d7e07baf02.png) Concurrent scheduling is a marketing term and simply means that there is a mechanism in place that knows how much time is spent in the current task. -This number is called frame budget and measured in milliseconds. As a result of this technique we're getting prioritized user-centric scheduling behaviours. +This number is called frame budget and measured in milliseconds. As a result of this technique we're getting prioritized user-centric scheduling behaviours. This enables: + - scheduling - cancellation - fine grained prioritization @@ -142,14 +145,14 @@ This enables: - render deadlines One of the first things to understand is the term "frame budget". -It means we have a maximum time (which is globally defined) a task can take before yielding to the main thread. e.g. 60frames/1000ms=16.6666ms animations or 50ms long task. +It means we have a maximum time (which is globally defined) a task can take before yielding to the main thread. e.g. 60frames/1000ms=16.6666ms animations or 50ms long task. Scheduling with notion of frame budget enables us to split work into individual browser tasks as soon as we exceed the budget. -We then yield to the main thread and are interactive again until the next batch of tasks will get processed. +We then yield to the main thread and are interactive again until the next batch of tasks will get processed. ![rx-angular-cdk-render-strategies__frame-budget](https://user-images.githubusercontent.com/10064416/115894224-4f098280-a459-11eb-9abf-9a902d66d380.png) -The special thing about the set of concurrent strategies is they have a render deadline. +The special thing about the set of concurrent strategies is they have a render deadline. It means if the scheduled tasks in the global queue of work is not exhausted after a certain time window, we stop the chunking process. Instead all remaining work will get executed as fast as possible. This means in one synchronous block (that potentially can causes a frame drop). @@ -196,10 +199,10 @@ Tooltips should be displayed immediately on mouse over. Any delay will be very n @Component({ selector: 'item-image', template: ` - `, }) @@ -209,21 +212,25 @@ export class ItemsListComponent { constructor(private strategyProvider: RxStrategyProvider) {} showTooltip() { - this.strategyProvider.schedule( - () => { - // create tooltip - }, - { strategy: 'immediate' } - ).subscribe(); + this.strategyProvider + .schedule( + () => { + // create tooltip + }, + { strategy: 'immediate' } + ) + .subscribe(); } hideTooltip() { - this.strategyProvider.schedule( - () => { - // destroy tooltip - }, - { strategy: 'immediate' } - ).subscribe(); + this.strategyProvider + .schedule( + () => { + // destroy tooltip + }, + { strategy: 'immediate' } + ) + .subscribe(); } } ``` @@ -231,7 +238,7 @@ export class ItemsListComponent { > **⚠ Notice:** > Be aware to avoid scheduling large or non-urgent work with immediate priority as it blocks rendering -### User Blocking +### User blocking ![render-strategies-concurrent-userBlocking-tree](https://user-images.githubusercontent.com/10064416/146285901-b84f4e32-9213-4674-9cfe-f4dffe68dd65.png) @@ -279,17 +286,19 @@ export class DropdownComponent { } hideDropdown() { - this.strategyProvider.schedule( - () => { - // destroy dropdown - }, - { strategy: 'userBlocking' } - ).subscribe(); + this.strategyProvider + .schedule( + () => { + // destroy dropdown + }, + { strategy: 'userBlocking' } + ) + .subscribe(); } } ``` -> **⚠ Notice:** +> **⚠ Notice:** > Be aware to avoid scheduling large or non-urgent work with `userBlocking` priority as it blocks rendering after 250ms ### Normal @@ -310,7 +319,7 @@ Heavy work visible to the user. For example, since it has a higher timeout, it i ![Render Strategies - normal example](https://user-images.githubusercontent.com/10064416/146285878-3b242f2d-046e-49ad-be2f-cbf1c33b7a02.png) -For `normal` strategy a perfect example will be rendering of the items list. +For `normal` strategy a perfect example will be rendering of the items list. It is often the case that rendering of big lists blocks user interactions. In combination with `rxFor` directive such operations become truly unblocking. @@ -373,22 +382,26 @@ export class ItemsListComponent { ) {} openCreateItemPopup() { - this.strategyProvider.schedule(() => { - // logic to lazy load popup component - }, {strategy: 'low'}).subscribe(); + this.strategyProvider + .schedule( + () => { + // logic to lazy load popup component + }, + { strategy: 'low' } + ) + .subscribe(); } - } ``` -> **⚠ Notice:** +> **⚠ Notice:** > This priority fits well for things that should happen but has lower priority. For any non-urgent background process `idle` is the best fit. ### Idle ![render-strategies-concurrent-idle-tree](https://user-images.githubusercontent.com/10064416/146285889-8f98a92c-35dd-4632-9b3a-0eaf759790fc.png) -Urgent work that should happen in the background and is not initiated but visible by the user. This occurs right after current task and has the lowest priority. +Urgent work that should happen in the background and is not initiated but visible by the user. This occurs right after current task and has the lowest priority. | Render Method | Scheduling | Render Deadline | | ----------------- | ------------- | --------------- | @@ -425,22 +438,28 @@ export class ItemsListComponent { private strategyProvider: RxStrategyProvider, private webSocket: WebSocketService ) { - this.items$.pipe( - this.strategyProvider.scheduleWith( - items => this.webSocket.syncItems(items), - {strategy: 'idle'} - ) - ).subscribe(); + this.items$ + .pipe( + this.strategyProvider.scheduleWith( + (items) => this.webSocket.syncItems(items), + { strategy: 'idle' } + ) + ) + .subscribe(); } openCreateItemPopup() { - this.strategyProvider.schedule(() => { - // logic to lazy load popup component - }, {strategy: 'low'}).subscribe(); + this.strategyProvider + .schedule( + () => { + // logic to lazy load popup component + }, + { strategy: 'low' } + ) + .subscribe(); } - } ``` -> **⚠ Notice:** +> **⚠ Notice:** > This priority fits well for low priority background processes that are not affecting user interactions. diff --git a/libs/cdk/render-strategies/docs/strategies.md b/apps/docs/docs/cdk/render-strategies/strategies/strategies.mdx similarity index 91% rename from libs/cdk/render-strategies/docs/strategies.md rename to apps/docs/docs/cdk/render-strategies/strategies/strategies.mdx index fd9ff95123..718530bf18 100644 --- a/libs/cdk/render-strategies/docs/strategies.md +++ b/apps/docs/docs/cdk/render-strategies/strategies/strategies.mdx @@ -1,3 +1,9 @@ +--- +sidebar_label: 'Strategies' +sidebar_position: 1 +hide_title: true +--- + # Strategies Before we go into detail with the provided strategies, let's understand angulars vanilla behaviour first. @@ -19,13 +25,15 @@ In many cases, this leads to heavy refactorings to get the performance flaw out Strategies give us a way to control how Angular's rendering is executed and which render method is used. -**Strategy Sets:** -- [Basic Strategies](https://github.com/rx-angular/rx-angular/blob/master/libs/cdk/render-strategies/docs/basic-strategies.md) -- [Concurrent Strategies](https://github.com/rx-angular/rx-angular/blob/master/libs/cdk/render-strategies/docs/concurrent-strategies.md) +**Strategy sets:** + +- [Basic strategies](basic-strategies.md) +- [Concurrent strategies](concurrent-strategies.md) ## Usage ### Configure existing features + ```typescript @Component({ selector: 'immediate', @@ -58,8 +66,7 @@ export class RenderCallbackComponent { } ``` - -### Custom Strategies +### Custom strategies ```typescript export type RxRenderWork = ( diff --git a/apps/docs/docs/cdk/template-management/template-management.mdx b/apps/docs/docs/cdk/template-management/template-management.mdx new file mode 100644 index 0000000000..162133509f --- /dev/null +++ b/apps/docs/docs/cdk/template-management/template-management.mdx @@ -0,0 +1,17 @@ +--- +sidebar_label: 'Template management' +sidebar_position: 1 +title: 'Template management' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/cdk/template/README.md'; + + + + + + +export const toc = [...readmeToc]; diff --git a/apps/docs/docs/cdk/transformations/transformations.mdx b/apps/docs/docs/cdk/transformations/transformations.mdx new file mode 100644 index 0000000000..ec3c1a265e --- /dev/null +++ b/apps/docs/docs/cdk/transformations/transformations.mdx @@ -0,0 +1,14 @@ +--- +sidebar_label: 'Transformations' +sidebar_position: 1 +title: 'Transformations' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/cdk/transformations/README.md'; + + + +export const toc = [...readmeToc]; diff --git a/libs/cdk/zone-configurations/docs/zone-flags.md b/apps/docs/docs/cdk/zone-configurations/_docs.md similarity index 97% rename from libs/cdk/zone-configurations/docs/zone-flags.md rename to apps/docs/docs/cdk/zone-configurations/_docs.md index 9ddf1f0ada..7e6935407a 100644 --- a/libs/cdk/zone-configurations/docs/zone-flags.md +++ b/apps/docs/docs/cdk/zone-configurations/_docs.md @@ -1,9 +1,9 @@ -# Resources +## Resources -**Example applications:** +**Example applications:** A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angular-cdk-zone-configuration). -# Motivation +## Motivation **How zone.js works by default** @@ -19,7 +19,7 @@ Zone can be [disabled fully](https://angular.io/guide/zone#disabling-zone), but To set up zone flags we can use the direct `window` properties as documented in the [official docs](https://angular.io/guide/zone#setting-up-zonejs). The best documentation can be found in the source [zone-flags](https://github.com/angular/angular/blob/master/packages/zone.js/lib/zone.configurations.api.ts). -# Available Approaches +## Available approaches In the following, we will discuss the vanilla JavaScript approach without any abstraction or DX to understand the fundamentals, as well as serve an approach for a setup with RxAngulars `@rx-angular/cdk/zone-config` helper. @@ -35,7 +35,7 @@ RxAngular should be our go-to approach as it serves as a more convenient way to - ✅ Assertion if zone-flags are not correctly used - ✅ Convenience methods -# Impact +## Impact Zone flags can land an incredible performance improvement, but also can cause a lot of problems related to change detection if it was not in a proper state. @@ -49,7 +49,7 @@ A second charts is with timer, scroll and xhr events turned off. ![angular and zone flags performance comparison](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/zone-configurations/docs/images/angular-zone-flags_performance-comparison_michael-hladky.png) -# Risks & What can break +## Risks & what can break Applications that rely on global state management are in a good position because they rely on push-based change propagation already. Therefore there is no reason for the dirty marking from the root. With some specific local state management, it is manageable to fix possible bugs quite easily for most of the flags. @@ -59,7 +59,7 @@ Another risk with zone flags is it will also affect **third-party libs** that re In general, if you turn off some flags, nothing may break cause others are still present. And only when 70% of flags would be turned off, you'd see some major regression in UI. That's why important not only to disable the flag and check that app is ok but also to go over the codebase checking that change detection would be triggered correctly without zone. -# Migration and Precondition +## Migration and precondition 1. Make sure `ChangeDetectionStrategy.OnPush` is set for the app's root component and the app is working properly. This is needed to ensure an immutable change propagation. 2. To prioritize the flags we need to compare we can ask the following questions: @@ -81,7 +81,7 @@ That's why important not only to disable the flag and check that app is ok but a 7. Take flame chart measurements or use breakpoints to ensure they are working 8. Test your application and see everything works fine -# Alternatives +## Alternatives An even **less granular way** to disable zone functionality is turning it off completely. diff --git a/libs/cdk/zone-configurations/docs/how-to-debug-zone-flags.md b/apps/docs/docs/cdk/zone-configurations/how-to-debug-zone-flags.md similarity index 95% rename from libs/cdk/zone-configurations/docs/how-to-debug-zone-flags.md rename to apps/docs/docs/cdk/zone-configurations/how-to-debug-zone-flags.md index fe314553f0..107844211d 100644 --- a/libs/cdk/zone-configurations/docs/how-to-debug-zone-flags.md +++ b/apps/docs/docs/cdk/zone-configurations/how-to-debug-zone-flags.md @@ -76,7 +76,7 @@ The check if the flag is set should now return true: console.log(window.__Zone_disable_timers); // logs 'false' if the flag is active ``` -![Log timers flag active](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/zone-configurations/docs/images/angular_zone-flags_disable-timers-true_michael-hladky.png) +![Log timers flag active](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/zone-configurations/docs/images/angular_zone-flags_disable-timers-true_michael-hladky.png) Now, `globalThis` should have no original/unpatched version present under the zone symbol. @@ -86,7 +86,7 @@ console.log(window.__zone_symbol__setTimeout); console.log(window.__zone_symbol__clearTimeout); ``` -![Log unpatched timer APIs](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/zone-configurations/docs/images/angular_zone-flags_setTimeout-unpatched_michael-hladky.png) +![Log unpatched timer APIs](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/cdk/zone-configurations/docs/images/angular_zone-flags_setTimeout-unpatched_michael-hladky.png) > **💡 Pro Tipps:** > diff --git a/libs/cdk/zone-configurations/docs/how-to-setup-zone-flags.md b/apps/docs/docs/cdk/zone-configurations/how-to-setup-zone-flags.md similarity index 99% rename from libs/cdk/zone-configurations/docs/how-to-setup-zone-flags.md rename to apps/docs/docs/cdk/zone-configurations/how-to-setup-zone-flags.md index 4990854ec2..48b62ac85b 100644 --- a/libs/cdk/zone-configurations/docs/how-to-setup-zone-flags.md +++ b/apps/docs/docs/cdk/zone-configurations/how-to-setup-zone-flags.md @@ -1,7 +1,5 @@ # How to set up zone flags ---- - ## Resources **Example application:** @@ -9,7 +7,7 @@ A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angu ## Concepts -### The Patching Mechanism +### The patching mechanism API patching or monkey-patching means we take an existing API and override its behavior globally or in specific places. @@ -37,7 +35,7 @@ EventTarget.prototype.addEventListener = patchedAddEventListener; Here we patch the global `addEventListener` API. Every fired event in the Browser will now pass our patch from above. -### The Flagging Mechanism +### The flagging mechanism When `zone.js` is first time initialised on the page, it takes values of flags already located in `window` object. So it's important to set them **before `zone.js` is init**. So we need to inject `zone-flags.js` code **above** the zone code. diff --git a/apps/docs/docs/cdk/zone-configurations/zone-configurations.mdx b/apps/docs/docs/cdk/zone-configurations/zone-configurations.mdx new file mode 100644 index 0000000000..a93458b054 --- /dev/null +++ b/apps/docs/docs/cdk/zone-configurations/zone-configurations.mdx @@ -0,0 +1,20 @@ +--- +sidebar_label: 'Zone configurations' +sidebar_position: 1 +title: 'Zone configurations' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/cdk/zone-configurations/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/libs/cdk/zone-less/docs/README.md b/apps/docs/docs/cdk/zone-less/_docs.md similarity index 82% rename from libs/cdk/zone-less/docs/README.md rename to apps/docs/docs/cdk/zone-less/_docs.md index 4ce52abfcc..434d743220 100644 --- a/libs/cdk/zone-less/docs/README.md +++ b/apps/docs/docs/cdk/zone-less/_docs.md @@ -1,21 +1,21 @@ -# Resources +## Resources -**Example applications:** +**Example applications:** A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angular-cdk-zone-less). -# Motivation +## Motivation -By default, Angular or better say zone.js patches most of the asynchronouse API's of the Browser. -This adds additional overhead on those API's and even more important, this leads to un-nessecary renderings of the Angular component tree, also known as overredering. +By default, Angular or better say zone.js patches most of the asynchronouse API's of the Browser. +This adds additional overhead on those API's and even more important, this leads to un-nessecary renderings of the Angular component tree, also known as overredering. Until now developers where only able to either disable zone.js completely or wrapping another logic around the patched APIs to avoid change detection calls. This of course requires to inject another service and also makes your code more cluttery and slow. The zone-less package helps out here. -It provides a general method to access the unpatched version of an API and also ships a set of commonly used APIs of the Browser as well as RxJS. +It provides a general method to access the unpatched version of an API and also ships a set of commonly used APIs of the Browser as well as RxJS. All those APIs are fully independent of zone.js and NgZone (which is normally used to run things outside Angular). -# The Benefits +## The benefits - ✅ Finegrained ways to unpatch APIs - ✅ Fastest option possible to run code outside of Angulars zone mechanism @@ -23,19 +23,19 @@ All those APIs are fully independent of zone.js and NgZone (which is normally us - ✅ Drop-in replacement for RxJS functions - ✅ No need for `runOutsideAngular` and `NgZone` injection -# RxAngular CDK/Zone-Less +## RxAngular CDK/Zone-less The way how zone.js works is, it patches all relevant async APIs of the Browser. This happens early on as zone.js is treated as polyfill. ![rx-angular-cdk-zone-less_API-patching_michael-hladky](https://user-images.githubusercontent.com/10064416/129472845-e27c5a52-f99d-4f5f-b205-4e947e188d25.png) All original logic of the patched API's is stored under a symbol in `window`. You can check that by logging for example the following value: `console.log(window.__zone_symbol__setTimeout)`. -This will print the original unpatched API to the Brwosers console. +This will print the original unpatched API to the Brwosers console. -Internally the symbols and in turn the unpatched API's is what the zone-less package is using. +Internally the symbols and in turn the unpatched API's is what the zone-less package is using. The essential method used to get hold of the unpatched APIs is called `getZoneUnPatchedApi`. It takes a target i.e. `window` and a name of the method we want to retreive the unpatched version of. -## Setup +### Setup The zone-less API's can be used directly from the `cdk` package. To do so, install the `cdk` package and, if needed, the packages depending on it: @@ -48,20 +48,18 @@ npm i @rx-angular/cdk yarn add @rx-angular/cdk ``` -## Usage +### Usage -The utils folder contains the most essential functions used to unpatch APIs. -Normally if developers want to avoid change detection through zone the have to inject `NgZone` and run the code that should not trigger change detection in the `runOutsideAngular` method. +The utils folder contains the most essential functions used to unpatch APIs. +Normally if developers want to avoid change detection through zone the have to inject `NgZone` and run the code that should not trigger change detection in the `runOutsideAngular` method. ```typescript export class AnyComponent { - constructor(private ngZone: NgZone) { - this.ngZone.runOutsideAngular(() => { + this.ngZone.runOutsideAngular(() => { // code here }); } - } ``` @@ -71,27 +69,24 @@ This introduces some lines of code and also takes time to retreive the service f - getZoneUnPatchedApi -The `getZoneUnPatchedApi` function is used in all other functions of the zone-less package to access the Browsers unpatched API's. - +The `getZoneUnPatchedApi` function is used in all other functions of the zone-less package to access the Browsers unpatched API's. ```typescript @Component({ // ... }) export class AppComponent { - doStuff() { - // unpatched method of the window object - getZoneUnPatchedApi('setTimeout')( - () => console.log('tada!'), - 300); - }; - + // unpatched method of the window object + getZoneUnPatchedApi('setTimeout')(() => console.log('tada!'), 300); + } + doOtherStuff() { // unpatched method of an HTML element - getZoneUnPatchedApi(elem, 'addEventListener')('click', () => console.log('tada!') ); - }; - + getZoneUnPatchedApi(elem, 'addEventListener')('click', () => + console.log('tada!') + ); + } } ``` @@ -113,14 +108,14 @@ import {setTimeout} from @rx-angular/cdk/zone-less // ... }) export class AppComponent { - + doStuff() { // unpatched method of the window object setTimeout( - () => console.log('tada!'), + () => console.log('tada!'), 300); }; - + } ``` @@ -139,7 +134,7 @@ scheduler - queue - animationFrame -As RxJS internally uses Browser API's it is pretty impossible to get rid of the zone involvment. +As RxJS internally uses Browser API's it is pretty impossible to get rid of the zone involvment. For this very reason we shipped the RxJS operators, schedulers and functions that would trigger zone as unpatched version. ```typescript @@ -149,22 +144,21 @@ import {interval} from @rx-angular/cdk/zone-less // ... }) export class AppComponent { - + doStuff() { // unpatched method of RxJS lib interval(300).subscribe(); }; - + } ``` - -# Available Approaches +## Available approaches - `ngZone#runOutsideZone` - `zone-configuration` -## `ngZone#runOutsideAngular` +### `ngZone#runOutsideAngular` It is possible to inject an instance of NgZone into components and services etc to tell Angular to run a specific peice of coe outside of Angular. @@ -173,27 +167,22 @@ It is possible to inject an instance of NgZone into components and services etc // ... }) export class AppComponent { - - constructor(private ngZone: NgZone) { - } - + constructor(private ngZone: NgZone) {} + doStuff() { - this.ngZone.runOutsideAngular(() => { setTimeout(() => { console.log('tada!'); - }, 300) + }, 300); }); - } - } ``` The downside here is we need to inject `NgZone` and rely on dependency injection wich is not only more code but also slow. -## Zone Configuration +### Zone Configuration -Zone configuration is a less granular way to disable zone. It helps to cinfigure zone in a way where it don't patches specific API's at all. +Zone configuration is a less granular way to disable zone. It helps to configure zone in a way where it don't patches specific APIs at all. -You can read in detail about it in the docs of [`@rx-angular/cdk/zone-configuration`](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/zone-configurations/docs/zone-flags.md). +You can read in detail about it in the docs of [`@rx-angular/cdk/zone-configuration`](../zone-configurations/zone-configurations.mdx). diff --git a/apps/docs/docs/cdk/zone-less/zone-less.mdx b/apps/docs/docs/cdk/zone-less/zone-less.mdx new file mode 100644 index 0000000000..2955956de5 --- /dev/null +++ b/apps/docs/docs/cdk/zone-less/zone-less.mdx @@ -0,0 +1,20 @@ +--- +sidebar_label: 'Zone-less' +sidebar_position: 1 +title: 'Zone-less' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/cdk/zone-less/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/apps/docs/docs/contributing.mdx b/apps/docs/docs/contributing.mdx new file mode 100644 index 0000000000..fec1659c8b --- /dev/null +++ b/apps/docs/docs/contributing.mdx @@ -0,0 +1,16 @@ +--- +sidebar_label: 'Contributing' +title: 'Contributing' +hide_title: true +--- + +import Contributing, { + toc as contributingToc, +} from '@site/../../CONTRIBUTING.md'; + + + + + + +export const toc = [...contributingToc]; diff --git a/apps/docs/docs/eslint-plugin/_category_.json b/apps/docs/docs/eslint-plugin/_category_.json new file mode 100644 index 0000000000..098a426f26 --- /dev/null +++ b/apps/docs/docs/eslint-plugin/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "@rx-angular/eslint-plugin" +} diff --git a/apps/docs/docs/eslint-plugin/eslint-plugin.mdx b/apps/docs/docs/eslint-plugin/eslint-plugin.mdx new file mode 100644 index 0000000000..2d9579bbee --- /dev/null +++ b/apps/docs/docs/eslint-plugin/eslint-plugin.mdx @@ -0,0 +1,17 @@ +--- +sidebar_label: '@rx-angular/eslint-plugin' +sidebar_position: 1 +title: 'ESLint plugin' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/eslint-plugin/README.md'; + + + + + + +export const toc = [...readmeToc]; diff --git a/apps/docs/docs/eslint-plugin/rules/_category_.json b/apps/docs/docs/eslint-plugin/rules/_category_.json new file mode 100644 index 0000000000..5ad72f43ce --- /dev/null +++ b/apps/docs/docs/eslint-plugin/rules/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "Rules", + "position": 1, + "link": { + "type": "generated-index", + "slug": "eslint-plugin/rules", + "title": "ESLint rules" + } +} diff --git a/libs/eslint-plugin/docs/rules/no-explicit-change-detection-apis.md b/apps/docs/docs/eslint-plugin/rules/no-explicit-change-detection-apis.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-explicit-change-detection-apis.md rename to apps/docs/docs/eslint-plugin/rules/no-explicit-change-detection-apis.md diff --git a/libs/eslint-plugin/docs/rules/no-rxstate-imperative-in-reactive.md b/apps/docs/docs/eslint-plugin/rules/no-rxstate-imperative-in-reactive.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-rxstate-imperative-in-reactive.md rename to apps/docs/docs/eslint-plugin/rules/no-rxstate-imperative-in-reactive.md diff --git a/libs/eslint-plugin/docs/rules/no-rxstate-subscriptions-outside-constructor.md b/apps/docs/docs/eslint-plugin/rules/no-rxstate-subscriptions-outside-constructor.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-rxstate-subscriptions-outside-constructor.md rename to apps/docs/docs/eslint-plugin/rules/no-rxstate-subscriptions-outside-constructor.md diff --git a/libs/eslint-plugin/docs/rules/no-zone-critical-browser-apis.md b/apps/docs/docs/eslint-plugin/rules/no-zone-critical-browser-apis.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-zone-critical-browser-apis.md rename to apps/docs/docs/eslint-plugin/rules/no-zone-critical-browser-apis.md diff --git a/libs/eslint-plugin/docs/rules/no-zone-critical-lodash-apis.md b/apps/docs/docs/eslint-plugin/rules/no-zone-critical-lodash-apis.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-zone-critical-lodash-apis.md rename to apps/docs/docs/eslint-plugin/rules/no-zone-critical-lodash-apis.md diff --git a/libs/eslint-plugin/docs/rules/no-zone-critical-rxjs-creation-apis.md b/apps/docs/docs/eslint-plugin/rules/no-zone-critical-rxjs-creation-apis.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-zone-critical-rxjs-creation-apis.md rename to apps/docs/docs/eslint-plugin/rules/no-zone-critical-rxjs-creation-apis.md diff --git a/libs/eslint-plugin/docs/rules/no-zone-critical-rxjs-operators.md b/apps/docs/docs/eslint-plugin/rules/no-zone-critical-rxjs-operators.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-zone-critical-rxjs-operators.md rename to apps/docs/docs/eslint-plugin/rules/no-zone-critical-rxjs-operators.md diff --git a/libs/eslint-plugin/docs/rules/no-zone-critical-rxjs-schedulers.md b/apps/docs/docs/eslint-plugin/rules/no-zone-critical-rxjs-schedulers.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-zone-critical-rxjs-schedulers.md rename to apps/docs/docs/eslint-plugin/rules/no-zone-critical-rxjs-schedulers.md diff --git a/libs/eslint-plugin/docs/rules/no-zone-run-apis.md b/apps/docs/docs/eslint-plugin/rules/no-zone-run-apis.md similarity index 100% rename from libs/eslint-plugin/docs/rules/no-zone-run-apis.md rename to apps/docs/docs/eslint-plugin/rules/no-zone-run-apis.md diff --git a/libs/eslint-plugin/docs/rules/prefer-no-layout-sensitive-apis.md b/apps/docs/docs/eslint-plugin/rules/prefer-no-layout-sensitive-apis.md similarity index 100% rename from libs/eslint-plugin/docs/rules/prefer-no-layout-sensitive-apis.md rename to apps/docs/docs/eslint-plugin/rules/prefer-no-layout-sensitive-apis.md diff --git a/libs/eslint-plugin/docs/rules/prefer-no-lodash-clone-deep.md b/apps/docs/docs/eslint-plugin/rules/prefer-no-lodash-clone-deep.md similarity index 100% rename from libs/eslint-plugin/docs/rules/prefer-no-lodash-clone-deep.md rename to apps/docs/docs/eslint-plugin/rules/prefer-no-lodash-clone-deep.md diff --git a/libs/eslint-plugin/docs/rules/prefer-no-lodash-is-equal.md b/apps/docs/docs/eslint-plugin/rules/prefer-no-lodash-is-equal.md similarity index 100% rename from libs/eslint-plugin/docs/rules/prefer-no-lodash-is-equal.md rename to apps/docs/docs/eslint-plugin/rules/prefer-no-lodash-is-equal.md diff --git a/apps/docs/docs/state/_category_.json b/apps/docs/docs/state/_category_.json index 6b3aa07c72..979a643d40 100644 --- a/apps/docs/docs/state/_category_.json +++ b/apps/docs/docs/state/_category_.json @@ -1,8 +1,3 @@ { - "label": "@rx-angular/state", - "position": 2, - "link": { - "type": "doc", - "id": "state/getting-started/overview" - } + "label": "@rx-angular/state" } diff --git a/libs/state/actions/docs/Readme.md b/apps/docs/docs/state/actions/_docs.md similarity index 72% rename from libs/state/actions/docs/Readme.md rename to apps/docs/docs/state/actions/_docs.md index 66b66d20b2..a240149861 100644 --- a/libs/state/actions/docs/Readme.md +++ b/apps/docs/docs/state/actions/_docs.md @@ -1,11 +1,11 @@ -# Resources +## Resources **Example applications:** A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angular-state-actions). -# Motivation +## Motivation -Signals, or a more commonly used name actions, are a common part of state management and reactive systems in general. +Signals, or a more commonly used name actions, are a common part of state management and reactive systems in general. Even if `@rx-angular/state` provides `set` method, sometimes you need to add behaviour to your user input or incoming events. Subjects are normally used to implement this feature. This leads, especially in bigger applications, to a messy code that is bloated with Subjects. @@ -15,25 +15,30 @@ Let's have a look at this piece of code: ```typescript @Component({ template: ` - Search for: {{search$ | async}}
- + + `, }) class Component { private _submitBtn = new Subject(); private _searchInput = new Subject(); - + set submitBtn() { - _submitBtn.next() + _submitBtn.next(); } get submitBtn$() { return _searchInput.asObservable(); } set search(search: string) { - _searchInput.next(search) + _searchInput.next(search); } get search$() { return _searchInput.asObservable(); @@ -42,16 +47,16 @@ class Component { list$ = this.submitBtn$.pipe( withLatestFrom(this.search$), switchMap(([_, searchString]) => this.api.query(searchString)) - ) + ); - constructor(api: API) { } - + constructor(api: API) {} } ``` Just look at the amount of code we need to write for only 2 observable ui events. Downsides: + - Boilerplate for setter and getter - Transformations spreaded over class and template - Manual typing ov subjects and streams @@ -62,42 +67,43 @@ Imagine we could have configurable functions that return all UI logic typed unde ```typescript import { RxActionFactory } from '@rx-angular/state/actions'; interface UiActions { - submitBtn: void, - searchInput: string + submitBtn: void; + searchInput: string; } @Component({ template: ` - Search for: {{ui.search$ | async}}
- + `, - providers: [RxActionFactory] + providers: [RxActionFactory], }) class Component { - ui = this.factory.create({searchInput: (e) => e.target.value}); - + ui = this.factory.create({ searchInput: (e) => e.target.value }); + list$ = this.ui.submitBtn$.pipe( withLatestFrom(this.ui.search$), switchMap(([_, searchString]) => this.api.query(searchString)) - ) - - constructor( - api: API, - private factory: RxActionFactory - ) {} + ); + constructor(api: API, private factory: RxActionFactory) {} } ``` -# RxAngular Signals +## RxAngular Signals -This package helps to reduce code used to create composable action streams. +This package helps to reduce code used to create composable action streams. It mostly is used in combination with state management libs to handle user interaction and backend communication. -## Setup +### Setup The coalescing features can be used directly from the `cdk` package or indirectly through the `state` package. To do so, install the `cdk` package and, if needed, the packages depending on it: @@ -110,7 +116,7 @@ npm i @rx-angular/state yarn add @rx-angular/state ``` -## Basic usage +### Basic usage By using RxAngular Actions we can reduce the boilerplate significantly, to do so we can start by thinking about the specific section their events and event payload types: @@ -129,6 +135,7 @@ commands = getActions(); ``` The object can now be used to emit signals over setters: + ```typescript commands.refreshUser(value); commands.refreshList(value); @@ -136,6 +143,7 @@ commands.refreshGenres(value); ``` The emitted signals can be received over observable properties: + ```typescript refreshUser$ = commands.refreshUser$; refreshList$ = commands.refreshList$; @@ -143,47 +151,54 @@ refreshGenres$ = commands.refreshGenres$; ``` You can also emit multiple signals at once: + ```typescript - commands({refreshUser: true, refreshList: true}); +commands({ refreshUser: true, refreshList: true }); ``` If there is the need to make a combined signal you can also select multiple signals and get their emissions in one stream: + ```typescript refreshUserOrList$ = commands.$(['refreshUser', 'refreshList']); ``` -## Signals in components +### Signals in components In components/templates we can use signals to map user interaction as well as programmatic to effects or state changes. -This reduces the component class code as well as template. +This reduces the component class code as well as template. In addition, we can use it as a shorthand in the template and directly connect to action dispatching in the class. ```typescript interface UiActions { - submitBtn: void, - searchInput: string + submitBtn: void; + searchInput: string; } @Component({ template: ` - Search for: {{ui.search$ | async}}
- + `, - providers: [RxState, RxActionFactory] + providers: [RxState, RxActionFactory], }) class Component { - ui = this.factory.create({searchInput: (e) => e?.target?.value}); + ui = this.factory.create({ searchInput: (e) => e?.target?.value }); list$ = this.state.select('list'); submittedSearchQuery$ = this.ui.submitBtn$.pipe( - withLatestFrom(this.ui.search$), + withLatestFrom(this.ui.search$), map(([_, search]) => search), debounceTime(1500) ); - + constructor( private state: RxState, private factory: RxActionFactory, @@ -191,7 +206,7 @@ class Component { ) { super(); this.connect('list', this.globalState.refreshGenres$); - + this.state.hold(this.submittedSearchQuery$, this.globalState.refreshGenres); // Optional reactively: // this.globalState.connectRefreshGenres(this.submittedSearchQuery$); @@ -199,7 +214,7 @@ class Component { } ``` -### Using transforms +#### Using transforms Often we process `Events` from the template and occasionally also trigger those channels in the class programmatically. @@ -209,6 +224,7 @@ This is also true for the programmatic usage in the component class or a service To ease this pain we can manage this login with `transforms`. You can write you own transforms or use the predefined functions: + - preventDefault - stopPropagation - preventDefaultStopPropagation @@ -216,38 +232,43 @@ You can write you own transforms or use the predefined functions: ```typescript interface UiActions { - submitBtn: void, - searchInput: string + submitBtn: void; + searchInput: string; } @Component({ template: ` - Search for: {{ui.search$ | async}}
- + `, - providers: [RxState, RxActionFactory] + providers: [RxState, RxActionFactory], }) class Component { // (e) => e.target ? e.target.value : e - ui = this.factory.create({searchInput: eventValue}); + ui = this.factory.create({ searchInput: eventValue }); list$ = this.state.select('list'); submittedSearchQuery$ = this.ui.submitBtn$.pipe( - withLatestFrom(this.ui.search$), + withLatestFrom(this.ui.search$), map(([_, search]) => search), debounceTime(1500) ); - + constructor( private state: RxState, private factory: RxActionFactory, globalState: StateService ) { - super(); + super(); this.connect('list', this.globalState.refreshGenres$); - + this.state.hold(this.submittedSearchQuery$, this.globalState.refreshGenres); // Optional reactively: // this.globalState.connectRefreshGenres(this.submittedSearchQuery$); @@ -255,9 +276,9 @@ class Component { } ``` -## Usage for global services +### Usage for global services -In services, it comes in handy to have a minimal typed action system. +In services, it comes in handy to have a minimal typed action system. This helps to have them composable for further optimizations. Furthermore, we can still expose setters to trigger actions the imperative way. @@ -270,9 +291,8 @@ interface Commands { refreshGenres: string | number; } - @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class StateService extends RxState { private commands = new RxActionFactory().create(); @@ -284,26 +304,20 @@ export class StateService extends RxState { this.connect( 'genres', - this.commands.fetchGenres$.pipe( - exhaustMap(this.tmdb2Service.getGenres) - ) + this.commands.fetchGenres$.pipe(exhaustMap(this.tmdb2Service.getGenres)) ); } refreshGenres(genre: string): void { this.commands.fetchGenres(genre); } - + // Optionally the reactive way connectRefreshGenres(genre$: Observable): void { this.connect( 'genres', - this.commands.fetchGenres$.pipe( - exhaustMap(this.tmdb2Service.getGenres) - ) + this.commands.fetchGenres$.pipe(exhaustMap(this.tmdb2Service.getGenres)) ); } - } ``` - diff --git a/apps/docs/docs/state/actions/actions.mdx b/apps/docs/docs/state/actions/actions.mdx new file mode 100644 index 0000000000..2a8ca49cdb --- /dev/null +++ b/apps/docs/docs/state/actions/actions.mdx @@ -0,0 +1,20 @@ +--- +sidebar_label: 'Actions' +sidebar_position: 5 +title: 'Actions' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/state/actions/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/apps/docs/docs/state/api/_category_.json b/apps/docs/docs/state/api/_category_.json index 7af0c3c625..ae24843ed7 100644 --- a/apps/docs/docs/state/api/_category_.json +++ b/apps/docs/docs/state/api/_category_.json @@ -1,5 +1,9 @@ { "label": "API", - "position": 2, - "link": null + "position": 100, + "link": { + "type": "generated-index", + "title": "API reference", + "slug": "/state/api" + } } diff --git a/apps/docs/docs/state/api/interfaces/_category_.json b/apps/docs/docs/state/api/interfaces/_category_.json index 07d82120ef..4c672870e7 100644 --- a/apps/docs/docs/state/api/interfaces/_category_.json +++ b/apps/docs/docs/state/api/interfaces/_category_.json @@ -3,6 +3,7 @@ "position": 100, "link": { "type": "generated-index", - "description": "RxAngular State interfaces." + "description": "RxAngular State interfaces.", + "slug": "state/api/interfaces" } } diff --git a/apps/docs/docs/state/api/rx-state.md b/apps/docs/docs/state/api/rx-state.md index ec7dd07938..e2feb0e4e1 100644 --- a/apps/docs/docs/state/api/rx-state.md +++ b/apps/docs/docs/state/api/rx-state.md @@ -61,7 +61,7 @@ class RxState implements OnDestroy, Subscribable { ##### typeof: Observable<T> The unmodified state exposed as `Observable`. It is not shared, distinct or gets replayed. -Use the `$` property if you want to read the state without having applied [stateful](/docs/state/api/rxjs-operators/stateful) to it. +Use the `$` property if you want to read the state without having applied [stateful](rxjs-operators/stateful.md) to it. --- diff --git a/apps/docs/docs/state/api/rxjs-operators/_category_.json b/apps/docs/docs/state/api/rxjs-operators/_category_.json index bdb13df693..d4b3a1b66a 100644 --- a/apps/docs/docs/state/api/rxjs-operators/_category_.json +++ b/apps/docs/docs/state/api/rxjs-operators/_category_.json @@ -3,6 +3,7 @@ "position": 2, "link": { "type": "generated-index", - "description": "This set of operators can be used in combination with the RxState service or outside of it. The main goal is to optimize data transfer between components, services, and templates." + "description": "This set of operators can be used in combination with the RxState service or outside of it. The main goal is to optimize data transfer between components, services, and templates.", + "slug": "state/api/rxjs-operators" } } diff --git a/libs/state/docs/concepts-and-best-practices.md b/apps/docs/docs/state/concepts-and-best-practices/concepts-and-best-practices.mdx similarity index 87% rename from libs/state/docs/concepts-and-best-practices.md rename to apps/docs/docs/state/concepts-and-best-practices/concepts-and-best-practices.mdx index 007de6e189..d18783a8bd 100644 --- a/libs/state/docs/concepts-and-best-practices.md +++ b/apps/docs/docs/state/concepts-and-best-practices/concepts-and-best-practices.mdx @@ -1,6 +1,10 @@ +--- +sidebar_position: 70 +--- + # Concepts and best practices -## Component Shell and Folder +## Component shell and folder - `@Input` bindings are setters - `@Output` bindings are state derivations @@ -21,9 +25,9 @@ Good: {{ obj }} ``` -## Component Implementation Approach +## Component implementation approach -### Defining the Interface +### Defining the interface In a first step you want to setup the state interface. A property that should change the view of your component should find its place in the interface. View bindings and triggers, which in turn mutate your state, should be `Subjects`. @@ -33,12 +37,11 @@ _Derived state_ should be handled separately. **Example view interface**: ```typescript - interface MyState { - items: string[]; - listExpanded: boolean; - sortKey: string; - isAsc: boolean; + items: string[]; + listExpanded: boolean; + sortKey: string; + isAsc: boolean; } interface MyView { @@ -104,13 +107,13 @@ vm$ = this.state.select(); Observables and projection functions are named in a way that gives us information about the returned data structure. -Think in Source => transfor => state/effect +Think in Source => transform => state/effect Bad: ```typescript getSearchResults() => this.inputChange$.pipe( - switchMap((q) => fetchData(q)) + switchMap((q) => fetchData(q)) ) ``` @@ -121,8 +124,8 @@ this.toSearchResult ) toSearchResults(o) => o.pipe( - map((data) => { - switchMap((q) => fetchData(q)) - }) + map((data) => { + switchMap((q) => fetchData(q)) + }) ) ``` diff --git a/libs/state/docs/snippets/deriving-simple-state.md b/apps/docs/docs/state/concepts-and-best-practices/deriving-simple-state.md similarity index 100% rename from libs/state/docs/snippets/deriving-simple-state.md rename to apps/docs/docs/state/concepts-and-best-practices/deriving-simple-state.md diff --git a/libs/state/docs/snippets/get-nested-state-slices.md b/apps/docs/docs/state/concepts-and-best-practices/get-nested-state-slices.md similarity index 73% rename from libs/state/docs/snippets/get-nested-state-slices.md rename to apps/docs/docs/state/concepts-and-best-practices/get-nested-state-slices.md index de1a50efe4..da6f0fd68a 100644 --- a/libs/state/docs/snippets/get-nested-state-slices.md +++ b/apps/docs/docs/state/concepts-and-best-practices/get-nested-state-slices.md @@ -4,7 +4,7 @@ One very common tasks when deriving state is selecting a single value out of the If you are familiar with RxJS, you will know about the [`pluck` operator](https://rxjs-dev.firebaseapp.com/api/operators/pluck) which "plucks" out values from an object. Also, the [`map`](https://rxjs-dev.firebaseapp.com/api/operators/map) operator could be used for this. -As shown in [deriving simple state](./deriving-simple-state.md) we use `stateful` operator from `@rx-angular/state` to get basic observable handling right out of the box. +As shown in [deriving simple state](deriving-simple-state.md) we use `stateful` operator from `@rx-angular/state` to get basic observable handling right out of the box. ```typescript import { Observable } from 'rxjs'; @@ -15,7 +15,7 @@ const state$: Observable<{ person: { name: string } }>; const derivation3$ = state$.pipe(stateful(pluck('person', 'name'))); ``` -We could even save more code by using the [`select`]() operator which essentially is a combination of `stateful` and `pluck`. +We could even save more code by using the [`select`](../api/rxjs-operators/select) operator which essentially is a combination of `stateful` and `pluck`. ```typescript import { Observable } from 'rxjs'; diff --git a/apps/docs/docs/state/concepts-and-best-practices/reactive-terminology.md b/apps/docs/docs/state/concepts-and-best-practices/reactive-terminology.md new file mode 100644 index 0000000000..d6f7adfcde --- /dev/null +++ b/apps/docs/docs/state/concepts-and-best-practices/reactive-terminology.md @@ -0,0 +1 @@ +# Reactive terminology diff --git a/libs/state/effects/docs/Readme.md b/apps/docs/docs/state/effects/_docs.md similarity index 92% rename from libs/state/effects/docs/Readme.md rename to apps/docs/docs/state/effects/_docs.md index bc28891d6e..329f71bfdc 100644 --- a/libs/state/effects/docs/Readme.md +++ b/apps/docs/docs/state/effects/_docs.md @@ -1,12 +1,14 @@ -# Resources +## Resources **Example applications:** A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angular-state-rx-effects). -# Motivation +## Motivation + ![rx-angular--state--effects--motivation--michael-hladky](https://user-images.githubusercontent.com/10064416/154173406-47245226-e56a-43b1-aec6-bbf1efc535b9.png) Most of the side effects are related to rendering and change detection and done in the template by building blocks like: + - pipes - directives - component bindings @@ -14,24 +16,22 @@ Most of the side effects are related to rendering and change detection and done Some of the side effects are not related to the template and need to get handled in the component. For for async effect's like Promise or Observable it requires to maintain a cancellation logic. -> **Pro Tip:** +> **Pro Tip:** > In general, it's best to avoid the direct use of the `subscribe` API of RxJS at all. It may sound weird, as I'm pretty sure you are used to handle your subscriptions. You most probably store the `Subscription` object, add a `takeUntil` to hook it into the component lifecycle and avoid memory leaks etc. -Maybe even hacks where you subscribe to one Observable just to next into another subject. +Maybe even hacks where you subscribe to one Observable just to next into another subject. In RxAngular we found ways to avoid the `subscribe` API and in addition handle all of the above edge cases and more. This is the hidden secret of why all parts of RxAngular fit together so well. -However, sometimes we have to subscribe in the component to handle reactive side effects. +However, sometimes we have to subscribe in the component to handle reactive side effects. This leads to bloated code and potential risk of a memory leak, late subscriber and so on. - -> In the context of state management every piece of code which does not manipulate, +> In the context of state management every piece of code which does not manipulate, > transform or read state can be considered as side effect. - Side effects can be triggered by state changes but don't depend on state. Side effects (most of the time coming from subscriptions) always yield the potential of a memory leak if not cleaned up correctly. @@ -40,11 +40,9 @@ To accomplish this, we need to make sure to clean up every side effect in the `O **With `RxEffects` RxAngular introduces another light weight tool only designed to manage side-effects.** ---- +### Problem -## Problem - -Let's get the problem it solves into code so we can refactor it. +Let's get the problem it solves into code so we can refactor it. We start with the side effect and 2 ways to execute it: @@ -53,8 +51,8 @@ We start with the side effect and 2 ways to execute it: // ... }) export class FooComponent { - // The side effect (`console.log`) - private runSideEffect = (num: number) => console.log('number: '+num); + // The side effect (`console.log`) + private runSideEffect = (num: number) => console.log('number: ' + num); // The interval triggers our function including the side effect private effect$ = interval(1000); @@ -62,13 +60,13 @@ export class FooComponent { // The subscribe's next callback it used to wrap and execute the side effect effect$.subscribe(this.runSideEffect); - effect$.pipe( - // `tap` is used to wrap and execute the side effect - tap(this.runSideEffect) - ).subscribe(); - + effect$ + .pipe( + // `tap` is used to wrap and execute the side effect + tap(this.runSideEffect) + ) + .subscribe(); } - } ``` @@ -79,7 +77,6 @@ As we introduced a memory leak we have to setup some boilerplate code to handle // ... }) export class FooComponent implements OnDestroy { - // ⚠ Notice: The destroy hook must be reactive to use `takeUntil` private readonly destroy$ = new Subject(); @@ -100,11 +97,11 @@ export class FooComponent implements OnDestroy { ``` There are already a couple of things that are crucial: + - using the right `Subject` - unsubscribe on destroy - having the `takeUntil` operator as last operator in the chain - Another way would be using the `subscription` to run the cleanup logic. ```ts @@ -112,7 +109,6 @@ Another way would be using the `subscription` to run the cleanup logic. // ... }) export class FooComponent implements OnDestroy { - // ⚠ Notice: The created subscription must be stored to `unsubscribe` later private readonly subscription: Subscription; @@ -128,9 +124,9 @@ export class FooComponent implements OnDestroy { } ``` -## Solution +### Solution -In RxAngular we think the essential problem here is the call to `subscribe` itself. All `Subscription`s need to get unsubscribed manually which most of the time produces heavy boilerplate or even memory leaks if ignored or did wrong. +In RxAngular we think the essential problem here is the call to `subscribe` itself. All `Subscription`s need to get unsubscribed manually which most of the time produces heavy boilerplate or even memory leaks if ignored or did wrong. Like `RxState`, `RxEffects` is a local service provided by a component and thus tied to the components life cycle. We can manage `Observables` as reactive triggers for side effects or manage `Subscription`s which internally hold side effects. To also provide an imperative way for developers to unsubscribe from the side effect `register` returns an "asyncId" similar to `setTimeout`. @@ -165,28 +161,29 @@ In fact, it removes the necessity of the `subscribe`. This results in less boilerplate and a good guidance to resilient and ergonomic component architecture. Furthermore, the optional imperative methods help to glue third party libs and a mixed but clean code style in Angular. -# Concepts +## Concepts Let's have some fundamental thoughts on the concept of side effects and their reactive handling. Before we get any further, let's define two terms, _side effect_ and _pure function_. -## Referentially Transparent +### Referentially transparent + ![rx-angular--state--effects--concept-referentially-transparent--michael-hladky](https://user-images.githubusercontent.com/10064416/154173775-7900608a-3fd9-4c56-b583-3150709d622e.png) -A function is referentially transparent if: +A function is referentially transparent if: + - it is **pure** (output must be the same for the same inputs) - it's evaluation must have no **side effects** -## Pure function -![rx-angular--state--effects--concept-pure-function--michael-hladky](https://user-images.githubusercontent.com/10064416/153937480-b39debc4-b524-4c7b-8f46-bd7b67b4b334.png) - +### Pure function +![rx-angular--state--effects--concept-pure-function--michael-hladky](https://user-images.githubusercontent.com/10064416/153937480-b39debc4-b524-4c7b-8f46-bd7b67b4b334.png) A function is called pure if: - Its return value is the same for the same arguments, e.g. `function add(a, b) { return a + b}` - Its executed internal logic has no side effects -## Side Effect +### Side effect ![rx-angular--state--effects--concept-side-effect-free--michael-hladky](https://user-images.githubusercontent.com/10064416/154173856-39ba5362-9952-46f6-83bd-765e4511b326.png) @@ -195,7 +192,7 @@ A function has a _side effect_ if: - There's a mutation of local static variables, e.g. `this.prop = value` - Non-local variables are used -### Examples +#### Examples Let's look at a couple of examples that will make the above definitions easier to understand. @@ -215,10 +212,9 @@ let state = { isVisible: false }; let newState = sideEffectFn(state); function sideEffectFn(oldState) { -oldState.isVisible = true; -return oldState; + oldState.isVisible = true; + return oldState; } - ``` - I/O is changed @@ -236,7 +232,7 @@ function sideEffectFn(state) { As a good rule of thumb, you can consider every function without a return value to be a side effect. -## Anatomy +### Anatomy ![rx-angular--state--effects--motivation-building-blocks--michael-hladky](https://user-images.githubusercontent.com/10064416/154174526-aa1409cd-e16a-4e3d-b913-f77920ffc05e.png) @@ -253,15 +249,12 @@ In the previous examples, the trigger was the method call itself like here: providers: [RxEffects], }) export class FooComponent { - private runSideEffect = console.log; - private effect$ = interval(1000).pipe( - tap(this.runSideEffect) - ); - + private runSideEffect = console.log; + private effect$ = interval(1000).pipe(tap(this.runSideEffect)); + constructor(effects: RxEffects) { effects.register(this.effect$); } - } ``` @@ -274,9 +267,9 @@ Thus, you may use a render call or any other logic executed by the trigger as th providers: [RxEffects], }) export class FooComponent { - private runSideEffect = console.log; + private runSideEffect = console.log; private effect$ = interval(1000); - + constructor(effects: RxEffects) { effects.register(this.effect$, this.runSideEffect); } @@ -293,14 +286,14 @@ However, if we want to stop a particular side effect earlier we can do the follo }) export class FooComponent { private effect$ = interval(1000); - private effectId:number; + private effectId: number; constructor(effects: RxEffects) { - this.effectId = effects.register(this.effect$, console.log); + this.effectId = effects.register(this.effect$, console.log); } - + stop() { - this.effects.unregister(this.effectId); + this.effects.unregister(this.effectId); } } ``` @@ -329,7 +322,7 @@ nx migrate @rx-angular/state ![rx-angular--state--effects--motivation-when-to-use--michael-hladky](https://user-images.githubusercontent.com/10064416/154174403-5ab34eb8-68e4-40f9-95de-12a62784ac40.png) In this example we have a chart in our UI which should display live data of a REST API ;). -We have a small handle that shows and hides the chart. +We have a small handle that shows and hides the chart. To avoid data fetching when the chart is not visible we connect the side effect to the toggle state of the chart. ```typescript @@ -338,10 +331,10 @@ To avoid data fetching when the chart is not visible we connect the side effect providers: [RxEffects], }) export class FooComponent { - + chartVisible$ = new Subject(); chartData$ = this.ngRxStore.select(getListData()); - + pollingTrigger$ this.chartVisible$.pipe( switchMap(isPolling => isPolling ? interval(2000) : EMPTY) ); @@ -352,7 +345,7 @@ export class FooComponent { ) { effects.register(this.pollingTrigger$, () => this.ngRxStore.dispatch(refreshAction())); } - + } ``` @@ -371,6 +364,7 @@ effects.register(obs$, { next: doSideEffect }); // <- you can also tap into erro ``` You can even use it with promises or schedulers: + ```typescript effects.register(fetch('...'), doSideEffect); effects.register(animationFrameScheduler.schedule(action)); @@ -412,4 +406,3 @@ const customErrorHandler: ErrorHandler = { }); // ... ``` - diff --git a/apps/docs/docs/state/effects/effects.mdx b/apps/docs/docs/state/effects/effects.mdx new file mode 100644 index 0000000000..1e81c4232d --- /dev/null +++ b/apps/docs/docs/state/effects/effects.mdx @@ -0,0 +1,22 @@ +--- +sidebar_label: 'Effects' +sidebar_position: 5 +title: 'Effects' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/state/effects/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; + +; diff --git a/libs/state/docs/FAQ.md b/apps/docs/docs/state/faq/faq.md similarity index 73% rename from libs/state/docs/FAQ.md rename to apps/docs/docs/state/faq/faq.md index fbbdbcf111..d803f3fb39 100644 --- a/libs/state/docs/FAQ.md +++ b/apps/docs/docs/state/faq/faq.md @@ -1,3 +1,10 @@ +--- +sidebar_label: 'FAQ' +sidebar_position: 60 +title: 'FAQ' +hide_title: true +--- + # Frequently asked questions ## What's the difference between injecting and extending the `RxState` service? diff --git a/apps/docs/docs/state/getting-started/_category_.json b/apps/docs/docs/state/getting-started/_category_.json deleted file mode 100644 index a85c93e8a6..0000000000 --- a/apps/docs/docs/state/getting-started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Getting started", - "position": 1, - "link": null -} diff --git a/apps/docs/docs/state/getting-started/overview.md b/apps/docs/docs/state/getting-started/overview.md deleted file mode 100644 index 361da13d1a..0000000000 --- a/apps/docs/docs/state/getting-started/overview.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -sidebar_label: Overview -sidebar_position: 1 -title: Overview -# Copied from libs/state/README.md ---- - -# @rx-angular/state - -[![npm](https://img.shields.io/npm/v/%40rx-angular%2Fstate.svg)](https://www.npmjs.com/package/%40rx-angular%2Fstate) -![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) -[![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/state/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/state/lcov-report/index.html) - -## Reactive Component State for Angular - -RxState is a lightweight, flexible, strongly typed and tested tool dedicated to reduce the complexity of managing component state in Angular. - -![state logo](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/state/docs/images/state_logo.png) - -## Sub Modules - -- [🧩 Selections](https://github.com/rx-angular/rx-angular/blob/main/libs/state/selections/README.md) -- [☁ Effects](https://github.com/rx-angular/rx-angular/blob/main/libs/state/effects/README.md) - -## Intro Video - -[![intro-video_rx-angular--state-rx-state](https://user-images.githubusercontent.com/10064416/147395467-876ec499-645f-4f84-bde9-9bffaac22c62.PNG)](https://www.youtube.com/watch?v=CcQYj4V2IKw) - -## Description - -Developing modern, **reactive** user interfaces imposes a variety of challenging tasks. Naming some of those: - -- reacting to events from different sources -- transforming and composing state -- handling state lifetime -- handling subscriptions - -There are plenty of solutions available for managing these challenges on a **global level** (Akita, NgRx, NgXs, ...). -None of them is dedicated to targeting the particular needs of the **component level**. - -`@rx-angular/state` was specifically designed to give developers a tool for mastering **component state** without forcing -them to use complex design patterns. - -Its lightweight and intuitive API and the automatic subscription handling makes `@rx-angular/state` -the **perfect fit** for handling state in any Angular component. - -Using this library allows you to implement things like: - -- merge global into local state -- shared state selections -- subscription-less interaction -- hook into imperative functions (e.g. component lifecycle or HostBindings) - -with very little effort in any component. - -

- - -

- -## Key features - -- Slim and intuitive API -- Automated subscription handling -- Intuitive way for handling ViewModels -- Connect any Observable source to the state -- Partial state updates -- Reactive state selection -- Lazy state (no BehaviourSubject) -- Foundation for zone-less Angular applications - -## Install - -```bash -npm install --save @rx-angular/state -# or -yarn add @rx-angular/state -``` - -## Update - -If you are using `@rx-angular/state` already, please consider upgrading with the `@angular/cli update` command in order -to make sure all provided code migrations are processed properly. - -```bash -ng update @rx-angular/state -# or with nx -nx migrate @rx-angular/state -``` - -## Usage - -[Usage Documentation](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/usage.md) - -## Testing - -[Testing](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/testing.md) - -## API - -[API Documentation](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/api/overview.md) - -## Tutorials - -- [Basic Tutorial](https://github.com/rx-angular/rx-angular/tree/main/apps/demos/src/app/features/tutorials/basics) -- [Counter - StackBlitz](https://stackblitz.com/edit/rx-angular-state-counter-demo?file=src%2Fapp%2Fcounter%2Fcounter.component.ts) - -## Snippets - -- [Logic comparison - Increment a Value](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/logic-comparison--increment-a-value.md) -- [Loading state and data fetching](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/loading-state-and-data-fetching.md) -- [Passing Observables directly](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/passing-observables-directly.md) -- [How to run partial state updates](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/how-can-i-run-partial-state-updates.md) -- [Get nested state slices](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/get-nested-state-slices.md) -- [Deriving simple state](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/deriving-simple-state.md) -- [Composing state using NgRx selectors](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/composing-state-using-ngrx-selectors.md) -- [Manage entities using NgRx entity adapter](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/manage-collections-with-ngrx-entity.md) -- [BehaviorSubject vs RxState](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/behavior-subject-vs-rx-state.md) -- [Managing ViewModels with selectSlice](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/selecting-the-viewmodel.md) -- [Manage reactive HostBindings](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/hostbindings.md) -- [Difference between Global and Local state](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/global-state-vs-local-state.md) -- [Using RxState as Global State](https://github.com/rx-angular/rx-angular/blob/main/libs/state/docs/snippets/manage-global-state.md) - -## Videos - -[![intro-video_rx-angular--state-rx-state](https://user-images.githubusercontent.com/10064416/147395467-876ec499-645f-4f84-bde9-9bffaac22c62.PNG)_🎥 RxAngular State, The Component Reactive Store | Marmicode Tasting Session_](https://www.youtube.com/watch?v=CcQYj4V2IKw) - -[![tackling-component-state-reactively](https://user-images.githubusercontent.com/10064416/147395866-031704dc-837d-4d1f-82d6-e758e4cb9556.PNG)_🎥 Tackling Component State Reactively (Live Demo at 24:47)_](https://www.youtube.com/watch?v=I8uaHMs8rw0) - -- [🎥 Extending Angular for the Reactive Web](https://youtu.be/pkN6CeZ8h_U?t=5913) - -- [German content | 🎥 Michael explains rx-state to webdave_de (Livestream)](https://youtu.be/cKUFcY8QkYM) - -## Blogs/Documents - -- [💾 Research on Reactive Ephemeral State](https://dev.to/rxjs/research-on-reactive-ephemeral-state-in-component-oriented-frameworks-38lk) - -- [📜 Design Documents](https://hackmd.io/wVkWRc3XQWmtM6YcktRTrA) - -## OSS Example Applications - -- [📑 Fully-reactive Zone-Less Angular/Ionic Progressive Web Application](https://startrack-ng.web.app/search) - [Mike Hartington](https://twitter.com/mhartington) -- [📑 High performant zone-Less Angular Progressive Web Application](https://angular-movies-a12d3.web.app/list/category/popular) - [TasteJS](https://github.com/tastejs/angular-movies) -- [📑 Zone-Less Angular Application - Tour of heros](https://github.com/BioPhoton/tour-of-heroes) - [Michael_Hladky](https://twitter.com/Michael_Hladky) - -## Version Compatibility - -| Angular | RxJS | @rx-angular/state | -| ---------------------- | -------------------- | ----------------- | -| `14` | `^7.4.0` | `> 1.4.6` | -| `^12.0.0` or `^13.0.0` | `^6.5.5` or `^7.4.0` | `> 1.4.6` | -| `^11.0.0` | `^6.5.5` | `<= 1.4.6` | - -Regarding the compatibility to RxJs, we generally stick to the compatibilities of the angular framework itself. -All the packages support RxJs versions `^6.5.5` || `^7.4.0`. -For more information about the compatibilities of angular itself see this [gist](https://gist.github.com/LayZeeDK/c822cc812f75bb07b7c55d07ba2719b3) diff --git a/apps/docs/docs/state/integrations/_category_.json b/apps/docs/docs/state/integrations/_category_.json index 7b6396b1ba..b8d8c01d60 100644 --- a/apps/docs/docs/state/integrations/_category_.json +++ b/apps/docs/docs/state/integrations/_category_.json @@ -1,5 +1,5 @@ { "label": "Integrations", - "position": 4, + "position": 90, "link": null } diff --git a/apps/docs/docs/state/recipes/_category_.json b/apps/docs/docs/state/recipes/_category_.json index a91d8ba94e..e8eebd30ac 100644 --- a/apps/docs/docs/state/recipes/_category_.json +++ b/apps/docs/docs/state/recipes/_category_.json @@ -1,5 +1,5 @@ { "label": "Recipes", - "position": 3, + "position": 80, "link": null } diff --git a/apps/docs/docs/state/recipes/manage-viewmodel.md b/apps/docs/docs/state/recipes/manage-viewmodel.md index 1724ff4841..4e2adda670 100644 --- a/apps/docs/docs/state/recipes/manage-viewmodel.md +++ b/apps/docs/docs/state/recipes/manage-viewmodel.md @@ -7,7 +7,7 @@ title: Selecting the ViewModel # Selecting the ViewModel -Here are some useful strategies to properly handle `ViewModels` with `@rx-angular/state`. In this examples we will use standalone [`selectSlice`](https://github.com/rx-angular/rx-angular/blob/main/libs/state/docs/api/operators/select-slice.md) operator. +Here are some useful strategies to properly handle `ViewModels` with `@rx-angular/state`. In this examples we will use standalone [`selectSlice`](../api/rxjs-operators/select-slice.md) operator. Imagine the following setup: diff --git a/apps/docs/docs/state/selections/_docs.md b/apps/docs/docs/state/selections/_docs.md new file mode 100644 index 0000000000..9a851b2f20 --- /dev/null +++ b/apps/docs/docs/state/selections/_docs.md @@ -0,0 +1,9 @@ +## Motivation + +TBD + +- [`Interfaces`](api/interfaces/) +- [`distinctUntilSomeChanged`](../api/rxjs-operators/distinct-until-some-changed.md) +- [`select`](../api/rxjs-operators/select.md) +- [`selectSlice`](../api/rxjs-operators/select-slice.md) +- [`stateful`](../api/rxjs-operators/stateful.md) diff --git a/apps/docs/docs/state/selections/selections.mdx b/apps/docs/docs/state/selections/selections.mdx new file mode 100644 index 0000000000..b5cedaef19 --- /dev/null +++ b/apps/docs/docs/state/selections/selections.mdx @@ -0,0 +1,20 @@ +--- +sidebar_label: 'Selections' +sidebar_position: 5 +title: 'Selections' +hide_title: true +--- + +import Readme, { + toc as readmeToc, +} from '@site/../../libs/state/selections/README.md'; +import Docs, { toc as docsToc } from './_docs.md'; + + + + + + + + +export const toc = [...readmeToc, ...docsToc]; diff --git a/apps/docs/docs/state/getting-started/setup.md b/apps/docs/docs/state/setup.md similarity index 100% rename from apps/docs/docs/state/getting-started/setup.md rename to apps/docs/docs/state/setup.md diff --git a/apps/docs/docs/state/state.mdx b/apps/docs/docs/state/state.mdx new file mode 100644 index 0000000000..1b06121dd0 --- /dev/null +++ b/apps/docs/docs/state/state.mdx @@ -0,0 +1,15 @@ +--- +sidebar_label: '@rx-angular/state' +sidebar_position: 1 +title: 'State' +hide_title: true +--- + +import Readme, { toc as readmeToc } from '@site/../../libs/state/README.md'; + + + + + + +export const toc = [...readmeToc]; diff --git a/libs/state/docs/testing.md b/apps/docs/docs/state/testing/testing.md similarity index 92% rename from libs/state/docs/testing.md rename to apps/docs/docs/state/testing/testing.md index da3fa4ced0..1d1bb9b4c3 100644 --- a/libs/state/docs/testing.md +++ b/apps/docs/docs/state/testing/testing.md @@ -1,3 +1,7 @@ +--- +sidebar_position: 5 +--- + # Testing This document provides a brief overview on how to set up unit tests with `jest` when using the `@rx-angular/state` @@ -22,9 +26,9 @@ its own. `RxState` can be used in different styles which will affect the way how we can actually test, modify and access the respective component: -* local provider -* inheritance -* local creation +- local provider +- inheritance +- local creation **Local Provider** @@ -35,15 +39,13 @@ when testing your component. You'll see why in the next section. ```ts @Component({ selector: 'rx-angular-state-local-provider-test', - template: ` - {{ value$ | async }} - `, + template: ` {{ value$ | async }} `, providers: [RxState], }) export class RxStateInjectionComponent { value$ = this.state.select(); - - constructor(public state: RxState<{ foo: string; }>) {} + + constructor(public state: RxState<{ foo: string }>) {} } ``` @@ -57,7 +59,7 @@ you cannot replace the instance of `RxState` in a test environment. selector: 'rx-angular-state-inheritance-test', template: ` {{ value$ }} `, }) -export class RxStateInheritanceComponent extends RxState<{ foo: string; }> { +export class RxStateInheritanceComponent extends RxState<{ foo: string }> { value$ = this.select(); constructor() { @@ -77,7 +79,7 @@ you cannot replace the instance of `RxState` in a test environment. template: ` {{ value$ }} `, }) export class RxStateCreationComponent { - state = new RxState<{ foo: string; }>(); + state = new RxState<{ foo: string }>(); value$ = this.state.select(); } ``` @@ -85,7 +87,7 @@ export class RxStateCreationComponent { **Setup the test environment** The steps to set up a test environment for component testing involving `RxState` are no different -to any other component tests. +to any other component tests. ```ts describe('MyComponent', () => { @@ -120,7 +122,7 @@ describe('MyComponent', () => { let component: MyComponent; let fixture: ComponentFixture; let mockState: RxState<{ foo: string }>; - + beforeEach(() => { // create a mock for your test environment mockState = new RxState(); @@ -130,8 +132,8 @@ describe('MyComponent', () => { providers: [ { provide: RxState, - useValue: mockState - } + useValue: mockState, + }, ], teardown: { destroyAfterEach: true }, }); @@ -160,14 +162,14 @@ There are cases where you want to unit test your state transformations instead o Ideally, you already have decoupled your `RxState` from your component in your application. -In order to create a fully decoupled `RxState` instance, you can simply create an `@Injectable()` and +In order to create a fully decoupled `RxState` instance, you can simply create an `@Injectable()` and extend from `RxState`. ```ts @Injectable() -export class MyState extends RxState<{ foo: string; }> { +export class MyState extends RxState<{ foo: string }> { state$ = this.select(); - + setFoo(foo: string): void { this.set({ foo }); } @@ -179,14 +181,12 @@ The `MyState` service now can be used as local provided instance for your compon ```ts @Component({ selector: 'rx-angular-state-local-provider-test', - template: ` - {{ state$ | async }} - `, + template: ` {{ state$ | async }} `, providers: [MyState], }) export class RxStateInjectionComponent { state$ = this.state.state$; - + constructor(public state: MyState) {} } ``` @@ -199,11 +199,10 @@ In your `jest` setup you are now able to test your Service completely decoupled > You can find more information about the [`jestMatcher` here](https://github.com/rx-angular/rx-angular/blob/main/libs/test-helpers/src/lib/rx-marbles/jest.observable-matcher.ts). ```ts - describe('MyState', () => { let service: MyState; let testScheduler: TestScheduler; - + beforeEach(() => { // create a new instance for each test service = new MyState(); @@ -229,10 +228,8 @@ it('state should emit foo', () => { testScheduler.run(({ expectObservable }) => { service.setFoo('in a test'); expectObservable(service.select('foo')).toBe('(a)', { - a: 'in a test' + a: 'in a test', }); }); }); ``` - - diff --git a/apps/docs/docs/state/tutorials/_category_.json b/apps/docs/docs/state/tutorials/_category_.json index efb75a07d3..7ae8e6882b 100644 --- a/apps/docs/docs/state/tutorials/_category_.json +++ b/apps/docs/docs/state/tutorials/_category_.json @@ -1,5 +1,5 @@ { "label": "Tutorials", - "position": 2, + "position": 3, "link": null } diff --git a/apps/docs/docs/state/tutorials/basic-tutorial/03-output-bindings.md b/apps/docs/docs/state/tutorials/basic-tutorial/03-output-bindings.md index 83f2f575fa..8c92811e34 100644 --- a/apps/docs/docs/state/tutorials/basic-tutorial/03-output-bindings.md +++ b/apps/docs/docs/state/tutorials/basic-tutorial/03-output-bindings.md @@ -13,7 +13,7 @@ This section contains an [imperative code base][output-bindings.start.component. ## React to state changes from child components In this example, we will be using an expansion panel to display a list. -For the purpose of this tutorial, we identify the panel's open and close states as part of the component's state. +For the purpose of this tutorial, we identify the panel's open and close states as part of the component's state. We will also have to forward the changes to the component's `listExpandedChange` output binding. As it is essential to connect Observables to the state, there is a service method that deals with this specific issue. diff --git a/apps/docs/docs/state/tutorials/migrating-to-rxstate.md b/apps/docs/docs/state/tutorials/migrating-to-rxstate.md index 8b7fbe8e0c..8b0523e413 100644 --- a/apps/docs/docs/state/tutorials/migrating-to-rxstate.md +++ b/apps/docs/docs/state/tutorials/migrating-to-rxstate.md @@ -381,7 +381,7 @@ name$ = this.state.select('name'); tasks$ = this.state.select('tasks'); ``` -Visually it looks the same but the select operator provides a lot more than just passing keys. You can read about it [here](https://github.com/rx-angular/rx-angular/blob/main/libs/state/docs/api/rx-state.md#select). Also selection will be shareReplayed, distincted and undefined values will be filtered out. +Visually it looks the same but the select operator provides a lot more than just passing keys. You can read about it [here](https://rx-angular.io/docs/state/api/rx-state#select). Also selection will be shareReplayed, distincted and undefined values will be filtered out. **Updating state reactively** @@ -426,7 +426,7 @@ constructor(private api: TodoApiService, private state: RxState) { Note that we removed `withLatestFrom(this.tasks$)` in favor of the `projectionFunction` in `connect`. First we define fields to be updated, then the source of the changes and lastly we provide the `projectionFunction`. The functions' first first argument is the current state, the second is the change coming from -our source. More on possible `connect` variants [here](https://github.com/rx-angular/rx-angular/blob/main/libs/state/docs/api/rx-state.md#connect). +our source. More on possible `connect` variants [here](../api/rx-state.md#connect). **Full component code** diff --git a/apps/docs/docs/template/_category_.json b/apps/docs/docs/template/_category_.json index c47aa5b128..7fc19f8936 100644 --- a/apps/docs/docs/template/_category_.json +++ b/apps/docs/docs/template/_category_.json @@ -1,8 +1,3 @@ { - "label": "@rx-angular/template", - "position": 3, - "link": { - "type": "generated-index", - "description": "RxAngular Template" - } + "label": "@rx-angular/template" } diff --git a/apps/docs/docs/template/api/_category_.json b/apps/docs/docs/template/api/_category_.json new file mode 100644 index 0000000000..0c5a157a24 --- /dev/null +++ b/apps/docs/docs/template/api/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "API", + "position": 100, + "link": { + "type": "generated-index", + "title": "API reference", + "slug": "/template/api" + } +} diff --git a/apps/docs/docs/template/api/experimental/_category_.json b/apps/docs/docs/template/api/experimental/_category_.json new file mode 100644 index 0000000000..60b628dbad --- /dev/null +++ b/apps/docs/docs/template/api/experimental/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "Experimental API", + "position": 100, + "link": { + "type": "generated-index", + "title": "Experimental API reference", + "slug": "/template/experimental/api" + } +} diff --git a/libs/template/docs/experimental/rx-if-directive.md b/apps/docs/docs/template/api/experimental/rx-if-directive.md similarity index 52% rename from libs/template/docs/experimental/rx-if-directive.md rename to apps/docs/docs/template/api/experimental/rx-if-directive.md index 0b539963fe..18928d678b 100644 --- a/libs/template/docs/experimental/rx-if-directive.md +++ b/apps/docs/docs/template/api/experimental/rx-if-directive.md @@ -1,3 +1,3 @@ -## 🧪 RxIf Directive +# 🧪 RxIf directive _more info coming soon_ diff --git a/libs/template/docs/experimental/viewport-prio.md b/apps/docs/docs/template/api/experimental/viewport-prio-directive.md similarity index 78% rename from libs/template/docs/experimental/viewport-prio.md rename to apps/docs/docs/template/api/experimental/viewport-prio-directive.md index e1b0ea670a..3f3bfc061c 100644 --- a/libs/template/docs/experimental/viewport-prio.md +++ b/apps/docs/docs/template/api/experimental/viewport-prio-directive.md @@ -1,4 +1,4 @@ -## 🧪 ViewportPriority Directive +# 🧪 ViewportPrioDirective This directive limits renderings to only visible components. Should be used together with Noop strategy. diff --git a/libs/template/docs/api/let-directive.md b/apps/docs/docs/template/api/let-directive.md similarity index 91% rename from libs/template/docs/api/let-directive.md rename to apps/docs/docs/template/api/let-directive.md index 77f97d5f85..45c14b4628 100644 --- a/libs/template/docs/api/let-directive.md +++ b/apps/docs/docs/template/api/let-directive.md @@ -1,4 +1,8 @@ -## LetDirective +--- +sidebar_position: 1 +--- + +# LetDirective The `*rxLet` directive serves a convenient way of binding observables to a view context. Furthermore, it helps you structure view-related models into view context scope (DOM element's scope). @@ -8,7 +12,7 @@ of your component. The `LetDirective` will render its template and manage change So if the incoming `Observable` emits its value lazily (e.g. data coming from `Http`), your template will be rendered lazily as well. This can very positively impact the initial render performance of your application. -### Problems with `async` and `*ngIf` +## Problems with `async` and `*ngIf` In Angular, a way of binding an observable to the view could look like that: @@ -28,7 +32,7 @@ you want to create a zone-less application, the `AsyncPipe` won't work as desire with its own strategies to manage change detection every time a new notification is sent from the bound Observable. -### Features of `*rxLet` +## Features of `*rxLet` Included features for `*rxLet`: @@ -42,7 +46,7 @@ Included features for `*rxLet`: - distinct same values in a row (`distinctUntilChanged` operator), - display custom templates for different observable notifications (suspense, next, error, complete) -### Binding an Observable and using the view context +## Binding an Observable and using the view context The `*rxLet` directive takes over several things and makes it more convenient and safe to work with streams in the template: @@ -74,7 +78,7 @@ We can track the observables: ``` -### Using the template-binding +## Using the template-binding You can also use template anchors and display template's content for different observable states: @@ -120,14 +124,14 @@ class LetDirective implements OnInit, OnDestroy { ### strategies -##### typeof: StrategySelection +#### typeof: StrategySelection All strategies initialized and registered for the `LetDirective`. Pass a name of one the `strategies` to the `strategy` input to switch between them on the fly. ### rxLet -##### typeof: ObservableInput<U> | null | undefined +#### typeof: ObservableInput<U> | null | undefined The Observable to be bound to the context of a template. @@ -141,7 +145,7 @@ _Example_ ### strategy -##### typeof: string | Observable<string> | undefined +#### typeof: string | Observable<string> | undefined The rendering strategy to be used when rendering with the reactive context within a template. Use it to dynamically manage your rendering strategy. You can switch the strategies @@ -180,7 +184,7 @@ export class AppComponent { ### rxLetComplete -##### typeof: TemplateRef<LetViewContext<U | undefined | null> | null> +#### typeof: TemplateRef<LetViewContext<U | undefined | null> | null> A template to show if the bound Observable is in "complete" state. @@ -197,7 +201,7 @@ _Example_ ### rxLetError -##### typeof: TemplateRef<LetViewContext<U | undefined | null> | null> +#### typeof: TemplateRef<LetViewContext<U | undefined | null> | null> A template to show if the bound Observable is in "error" state. @@ -214,7 +218,7 @@ _Example_ ### rxLetSuspense -##### typeof: TemplateRef<LetViewContext<U | undefined | null> | null> +#### typeof: TemplateRef<LetViewContext<U | undefined | null> | null> A template to show before the first value is emitted from the bound Observable. diff --git a/libs/template/docs/api/push-pipe.md b/apps/docs/docs/template/api/push-pipe.md similarity index 81% rename from libs/template/docs/api/push-pipe.md rename to apps/docs/docs/template/api/push-pipe.md index 3794491fcc..1f4109a4a6 100644 --- a/libs/template/docs/api/push-pipe.md +++ b/apps/docs/docs/template/api/push-pipe.md @@ -1,3 +1,7 @@ +--- +sidebar_position: 1 +--- + # PushPipe The `push` pipe serves as a drop-in replacement for the `async` pipe. @@ -18,7 +22,7 @@ components and does not work in zone-less mode. ## Solution -`push` pipe solves that problem. It contains intelligent handling of change detection by leveraging a [RenderStrategy](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/render-strategies/README.md) under the hood, which in turn, takes care of optimizing the `ChangeDetection` of your component. The `push` pipe can be used in zone-full as well as zone-less mode without any changes to the code. +`push` pipe solves that problem. It contains intelligent handling of change detection by leveraging a [RenderStrategy](docs/cdk/render-strategies/render-strategies.mdx) under the hood, which in turn, takes care of optimizing the `ChangeDetection` of your component. The `push` pipe can be used in zone-full as well as zone-less mode without any changes to the code. _Example_ @@ -42,7 +46,7 @@ _Example_ - Handling null and undefined values in a clean unified/structured way - Distinct same values in a row to increase performance - Coalescing of change detection calls to boost performance -- Lazy rendering (see [LetDirective](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/let-directive.md)) +- Lazy rendering (see [LetDirective](let-directive.md)) - Chunked rendering ## Signature diff --git a/libs/template/docs/experimental/rx-for-directive.md b/apps/docs/docs/template/api/rx-for-directive.md similarity index 94% rename from libs/template/docs/experimental/rx-for-directive.md rename to apps/docs/docs/template/api/rx-for-directive.md index f2d79b7e67..a0441c19c4 100644 --- a/libs/template/docs/experimental/rx-for-directive.md +++ b/apps/docs/docs/template/api/rx-for-directive.md @@ -1,4 +1,8 @@ -## RxFor Directive +--- +sidebar_position: 1 +--- + +# RxFor directive The `*rxFor` structural directive provides a convenient and performant way for rendering templates out of a list of items. @@ -13,8 +17,7 @@ Furthermore, `RxFor` provides access to the rendering cycle and informs about an However, the rendering behavior is fully configurable and transparent for the developer. Each instance of `RxFor` can be configured to render with different settings. - -### Features of `*rxFor` +## Features of `*rxFor` Included features for `*rxFor`: @@ -29,7 +32,7 @@ Included features for `*rxFor`: - cancel any scheduled work if a remove was triggered for a `trackById` - cancel any update if a new update was triggered for the same `trackById` -### Simple example using `*rxFor` with `Observable` values +## Simple example using `*rxFor` with `Observable` values ```html
    @@ -37,7 +40,7 @@ Included features for `*rxFor`:
``` -### Simple example using `*rxFor` with simple static values +## Simple example using `*rxFor` with simple static values ```html
    @@ -45,7 +48,7 @@ Included features for `*rxFor`:
``` -### Context Variables +## Context Variables The following context variables are available for each template: @@ -81,7 +84,7 @@ This example showcases the `select` view-context function used for deeply nested ``` -### Input properties +## Input properties - trackBy: `(index: number, item: T) => any` - trackBy: `keyof T` @@ -90,7 +93,7 @@ This example showcases the `select` view-context function used for deeply nested - parent: `boolean`; - renderCallback: `Subject` -### Using the context variables +## Using the context variables ```html
    @@ -117,7 +120,7 @@ This example showcases the `select` view-context function used for deeply nested
``` -### Projected Views (`parent`) +## Projected Views (`parent`) Imagine the following situation: diff --git a/libs/template/docs/api/unpatch-directive.md b/apps/docs/docs/template/api/unpatch-directive.md similarity index 92% rename from libs/template/docs/api/unpatch-directive.md rename to apps/docs/docs/template/api/unpatch-directive.md index c8ffa9f05d..1eb8eba7f5 100644 --- a/libs/template/docs/api/unpatch-directive.md +++ b/apps/docs/docs/template/api/unpatch-directive.md @@ -1,4 +1,8 @@ -## 🧪 UnpatchDirective +--- +sidebar_position: 1 +--- + +# UnpatchDirective The `unpatch` directive helps developers to partially deactivate `NgZone`, as well as getting rid of unnecessary renderings through zones `addEventListener` patches. @@ -12,7 +16,7 @@ The current way of binding events to DOM: ``` The problem is that every event registered via `()`, e.g. `(mousemove)` (or custom `@Output()`) -marks the component and all its ancestors as dirty and re-renders the whole component tree. [read more about this here](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/performance-issues.md) +marks the component and all its ancestors as dirty and re-renders the whole component tree. [read more about this here](../concepts/performance-issues.md) So even if your eventListener is not related to any change at all, your app will re-render the whole component tree. This can lead to very bad user experiences, especially if you work with frequently fired events such as `mousemove`. diff --git a/apps/docs/docs/template/concepts/_category_.json b/apps/docs/docs/template/concepts/_category_.json new file mode 100644 index 0000000000..1f83dbd0d2 --- /dev/null +++ b/apps/docs/docs/template/concepts/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Concepts", + "position": 1 +} diff --git a/libs/template/docs/concepts.md b/apps/docs/docs/template/concepts/concepts.md similarity index 100% rename from libs/template/docs/concepts.md rename to apps/docs/docs/template/concepts/concepts.md diff --git a/libs/template/docs/performance-issues.md b/apps/docs/docs/template/concepts/performance-issues.md similarity index 93% rename from libs/template/docs/performance-issues.md rename to apps/docs/docs/template/concepts/performance-issues.md index 4b844e17d6..7064a83876 100644 --- a/libs/template/docs/performance-issues.md +++ b/apps/docs/docs/template/concepts/performance-issues.md @@ -1,10 +1,17 @@ -## Rendering Issues in Angular +--- +sidebar_label: 'Performance issues' +sidebar_position: 1 +title: 'Performance issues' +hide_title: true +--- + +# Rendering issues in Angular A brief overview about what is about the current situation in terms of rendering in angular applications. -![Scheduling Options](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/template/docs/images/scheduling-options.png) +![Scheduling options](https://raw.githubusercontent.com/rx-angular/rx-angular/main/libs/template/docs/images/scheduling-options.png) -### Binding Reactive Sources +## Binding reactive sources The current way of binding _reactive_ sources to a view in angular looks like that: @@ -30,7 +37,7 @@ software. The comprehensive toolset of `@rx-angular/template` solves most of those issues with or without `zone.js`. -### NgZone +## NgZone `NgZone` assumes that DOM events like click, resize, focus, blur (+ `EventEmitters`, `setTimeOut`, `Promise.resolve()`, etc) are always used by developers to dispatch actions which leads to state mutation. If one of those diff --git a/libs/template/docs/reactive-context.md b/apps/docs/docs/template/concepts/reactive-context.md similarity index 94% rename from libs/template/docs/reactive-context.md rename to apps/docs/docs/template/concepts/reactive-context.md index 6d0afb8065..52be7dfe19 100644 --- a/libs/template/docs/reactive-context.md +++ b/apps/docs/docs/template/concepts/reactive-context.md @@ -1,3 +1,10 @@ +--- +sidebar_label: 'Reactive context' +sidebar_position: 1 +title: 'Reactive context' +hide_title: true +--- + # The extended reactive context in RxAngular If we think about any process, e.g. an HTTP request, we can differentiate different states in it. @@ -118,8 +125,6 @@ export type RxNotification = Pick, NotificationExtract> & { }; ``` ---- - -As a sum up, we now know that `@rx-angular/template` provides an extended reactive context with the `suspense` channel. +To sum up, we now know that `@rx-angular/template` provides an extended reactive context with the `suspense` channel. Use suspense as a template wherever possible as it reduces rendering work drastically. Also, be sure to remember that we have also access to the values of the 4 channels as local variables in the template and as the notifications from the render callback. diff --git a/apps/docs/docs/template/template.mdx b/apps/docs/docs/template/template.mdx new file mode 100644 index 0000000000..a662fd36dc --- /dev/null +++ b/apps/docs/docs/template/template.mdx @@ -0,0 +1,15 @@ +--- +sidebar_label: '@rx-angular/template' +sidebar_position: 1 +title: 'Template' +hide_title: true +--- + +import Readme, { toc as readmeToc } from '@site/../../libs/template/README.md'; + + + + + + +export const toc = [...readmeToc]; diff --git a/apps/docs/docusaurus.config.js b/apps/docs/docusaurus.config.js index 2561e5fc59..8e7af1acdc 100644 --- a/apps/docs/docusaurus.config.js +++ b/apps/docs/docusaurus.config.js @@ -69,23 +69,29 @@ module.exports = { // position: 'left', // }, { - docId: 'cdk/api/transformation-helpers/index', + docId: 'cdk/cdk', label: 'CDK', position: 'left', type: 'doc', }, { - docId: 'state/getting-started/overview', + docId: 'eslint-plugin/eslint-plugin', + label: 'ESLint', + position: 'left', + type: 'doc', + }, + { + docId: 'state/state', label: 'State', position: 'left', type: 'doc', }, - // { - // docId: 'template/index', - // label: 'Template', - // position: 'left', - // type: 'doc', - // }, + { + docId: 'template/template', + label: 'Template', + position: 'left', + type: 'doc', + }, // { to: 'blog', label: 'Blog', position: 'left' }, { href: `https://github.com/${organizationName}/${projectName}`, @@ -108,16 +114,20 @@ module.exports = { items: [ { label: '@rx-angular/cdk', - to: 'docs/cdk/api/transformation-helpers', + to: 'docs/cdk/', + }, + { + label: '@rx-angular/eslint-plugin', + to: 'docs/eslint-plugin/', }, { label: '@rx-angular/state', - to: 'docs/state/getting-started/overview', + to: 'docs/state/', + }, + { + label: '@rx-angular/template', + to: 'docs/template/', }, - // { - // label: '@rx-angular/template', - // to: 'docs/template/', - // }, ], }, { diff --git a/apps/docs/src/components/HomepageFeatures/index.tsx b/apps/docs/src/components/HomepageFeatures/index.tsx index be87856834..ac6e115650 100644 --- a/apps/docs/src/components/HomepageFeatures/index.tsx +++ b/apps/docs/src/components/HomepageFeatures/index.tsx @@ -19,21 +19,21 @@ const FeatureList: FeatureItem[] = [ UI libs and large scale applications. ), - url: 'docs/cdk/api/transformation-helpers', + url: 'docs/cdk', }, { title: '@rx-angular/state', Svg: require('@site/static/img/undraw_rxangular_data_processing.svg') .default, description: <>Reactive Component State-Management., - url: 'docs/state/getting-started/overview', + url: 'docs/state', }, { title: '@rx-angular/template', Svg: require('@site/static/img/undraw_rxangular_progressive_app.svg') .default, description: <>High-Performance Reactive Template Rendering for Angular., - url: null, + url: 'docs/template', }, ]; diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx index b13be4b648..ba2060b724 100644 --- a/apps/docs/src/pages/index.tsx +++ b/apps/docs/src/pages/index.tsx @@ -18,7 +18,7 @@ function HomepageHeader(): JSX.Element {
Get Started diff --git a/libs/cdk/README.md b/libs/cdk/README.md index 9dad63ab3b..0dda463bed 100644 --- a/libs/cdk/README.md +++ b/libs/cdk/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A Component Development Kit for High performance and ergonomic Angular UI libs and large scale applications +> A Component Development Kit for High performance and ergonomic Angular UI libs and large scale applications `@rx-angular/cdk` was specifically designed to help developers build directives, components and services for ergonomic and high performant Angular UI libs as well as large scale applications @@ -13,16 +13,14 @@ applications ## Sub Modules - -- [⛔ Zone Configuration](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/zone-configurations) -- [🚫 Zone Less](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/zone-less) -- [🛠 Coercing](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coercing) -- [🛠 Coalescing](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/coalescing) -- [📡 Notifications](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/notifications) -- [🖌 Render-Strategies](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/render-strategies) -- [🔳 Template Management](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/template) -- [🔳 Transformations](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/transformations) - +- [⛔ Zone Configuration](https://rx-angular.io/docs/cdk/zone-configurations) +- [🚫 Zone Less](https://rx-angular.io/docs/cdk/zone-less) +- [🛠 Coercing](https://rx-angular.io/docs/cdk/coercing) +- [🛠 Coalescing](https://rx-angular.io/docs/cdk/coalescing) +- [📡 Notifications](https://rx-angular.io/docs/cdk/notifications) +- [🖌 Render-Strategies](https://rx-angular.io/docs/cdk/render-strategies) +- [🔳 Template Management](https://rx-angular.io/docs/cdk/template) +- [🔳 Transformations](https://rx-angular.io/docs/cdk/transformations) ## Demos: @@ -50,8 +48,8 @@ nx migrate @rx-angular/cdk ## Version Compatibility -| Angular | RxJS | @rx-angular/cdk | -|------------------------|----------------------|---------------------| +| Angular | RxJS | @rx-angular/cdk | +| ---------------------- | -------------------- | ------------------- | | `14` | `^7.4.0` | `> 1.0.0-alpha.10` | | `^12.0.0` or `^13.0.0` | `^6.5.5` or `^7.4.0` | `> 1.0.0-alpha.10` | | `^11.0.0` | `^6.5.5` | `<= 1.0.0-alpha.10` | diff --git a/libs/cdk/coalescing/README.md b/libs/cdk/coalescing/README.md index 92170ce1ad..6fbb540ef3 100644 --- a/libs/cdk/coalescing/README.md +++ b/libs/cdk/coalescing/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A small typed and documented convenience helper to apply performance inprovements over coalescing techniques. +> A small typed and documented convenience helper to apply performance improvements over coalescing techniques. `@rx-angular/cdk/coalescing` is designed to help developers improve performance in high frequeltly changing values. Besides a well documented and typed API it also provides a set of marble diagrams for better visual understanding as well as smart defaults. @@ -33,4 +33,4 @@ yarn add @rx-angular/cdk ## Documentation -- [Coalescing](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/coalescing/docs/Readme.md) +- [Coalescing](https://rx-angular.io/docs/cdk/coalesing) diff --git a/libs/cdk/coercing/README.md b/libs/cdk/coercing/README.md index 9514aafb48..48d104749e 100644 --- a/libs/cdk/coercing/README.md +++ b/libs/cdk/coercing/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## Convenience helper to easily coerce reactive values +> Convenience helper to easily coerce reactive values `@rx-angular/cdk/coercing` is a small set of helpers designed to improve the DX when managing transformations between imperative and reactive values. It provides a strictly typed API to conveniently coerce imperative setters into reactive streams. @@ -32,4 +32,4 @@ yarn add @rx-angular/cdk/coercing ## Documentation -- [Coercion](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/coercing/docs/Readme.md) +- [Coercion](https://rx-angular.io/docs/cdk/coercing) diff --git a/libs/cdk/notifications/README.md b/libs/cdk/notifications/README.md index 6683e02d56..3514f779d3 100644 --- a/libs/cdk/notifications/README.md +++ b/libs/cdk/notifications/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A small typed convenience helper to handle contextual state. +> A small typed convenience helper to handle contextual state. `@rx-angular/cdk/notifications` is a small set of helpers designed to handle contextual states display. @@ -28,5 +28,4 @@ yarn add @rx-angular/cdk ## Documentation -- [Notifications](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/notifications/docs/Readme.md) - +- [Notifications](http://rx-angular.io/docs/cdk/notifications) diff --git a/libs/cdk/render-strategies/README.md b/libs/cdk/render-strategies/README.md index 2e1af6f35a..796642e216 100644 --- a/libs/cdk/render-strategies/README.md +++ b/libs/cdk/render-strategies/README.md @@ -4,19 +4,20 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A small typed convenience helper to handle contextual state. +> A small typed convenience helper to handle contextual state. Convenience helper to easily apply different change detection mechanisms to Angular -@rx-angular/cdk/render-strategies is a small set of helpers designed to improve the DX when managing change detection and detached or zone-less templates. +@rx-angular/cdk/render-strategies is a small set of helpers designed to improve the DX when managing change detection and detached or zone-less templates. The documentation also includes detaillsd explainations of techniques and provides diagrams for better visual understanding. ## Key features + - ✅ A easy mental model for highly sophisticated techniques -- ✅ Abstracted ChangeDetection +- ✅ Abstracted ChangeDetection - ✅ Schedule any type of work - ✅ Rich set of optimized change detection strategies - -✅ Fully tested +- ✅ Fully tested - ✅ Well Documented ## Demos: @@ -33,8 +34,7 @@ yarn add @rx-angular/cdk ## Documentation -- [Render Strategies](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/render-strategies/docs/README.md) - - [Strategies](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/render-strategies/docs/strategies.md) - - [Basic Strategies](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/render-strategies/docs/basic-strategies.md) - - [Concurrent-strategies](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/render-strategies/docs/concurrent-strategies.md) - +- [Render Strategies](https://rx-angular.io/docs/cdk/render-strategies) + - [Strategies](https://rx-angular.io/docs/cdk/render-strategies/strategies) + - [Basic Strategies](https://rx-angular.io/docs/cdk/render-strategies/basic-strategies) + - [Concurrent-strategies](https://rx-angular.io/docs/cdk/render-strategies/concurrent-strategies) diff --git a/libs/cdk/template/Readme.md b/libs/cdk/template/README.md similarity index 75% rename from libs/cdk/template/Readme.md rename to libs/cdk/template/README.md index e07495f103..180d3cc391 100644 --- a/libs/cdk/template/Readme.md +++ b/libs/cdk/template/README.md @@ -4,13 +4,14 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A small typed convenience helper to handle contextual state. +> A small typed convenience helper to handle contextual state. Convenience helper to easily the demplate part of structuran directives in Angular @rx-angular/cdk/template is... ## Key features + ✅ A easy mental model for highly sophisticated techniques ... ✅ Fully tested @@ -30,5 +31,5 @@ yarn add @rx-angular/cdk ## Documentation -- [Template Manager](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/template/docs/template-manager.md) -- [List Manager](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/template/docs/list-manager.md) +- [Template Manager](https://rx-angular.io/docs/cdk/template/template-manager) +- [List Manager](https://rx-angular.io/docs/cdk/template/list-manager) diff --git a/libs/cdk/transformations/README.md b/libs/cdk/transformations/README.md index c0339d62c1..fb7362fef8 100644 --- a/libs/cdk/transformations/README.md +++ b/libs/cdk/transformations/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=master) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## Slogan +> Slogan `@rx-angular/cdk/transformations` TBD @@ -26,5 +26,4 @@ yarn add @rx-angular/cdk ## Documentation -- [Transformations](https://github.com/rx-angular/rx-angular/tree/master/libs/cdk/transformations/docs/Readme.md) - +- [Transformations](https://rx-angular.io/docs/cdk/transformations) diff --git a/libs/cdk/zone-configurations/README.md b/libs/cdk/zone-configurations/README.md index 563df226f2..65fc0e02d1 100644 --- a/libs/cdk/zone-configurations/README.md +++ b/libs/cdk/zone-configurations/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A small typed and documented convenience helper to configure `zone.js` patching over zone-flags. +> A small typed and documented convenience helper to configure `zone.js` patching over zone-flags. `@rx-angular/cdk/zone-configurations` is designed to help developers configure zone-flage easyly. Besides a well documented and typed API it also provides a set of convenience features. @@ -33,6 +33,6 @@ yarn add @rx-angular/cdk ## Documentation -- [Zone Configurations with ZoneFlags](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/zone-configurations/docs/zone-flags.md) - - [How to set up zone flags](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/zone-configurations/docs/how-to-setup-zone-flags.md) - - [How to debug zone flags](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/zone-configurations/docs/how-to-debug-zone-flags.md) +- [Zone Configurations with ZoneFlags](https://rx-angular.io/docs/cdk/zone-configurations/zone-flags) + - [How to set up zone flags](https://rx-angular.io/cdk/zone-configurations/how-to-debug-zone-flags) + - [How to debug zone flags](https://rx-angular.io/docs/cdk/zone-configurations/how-to-setup-zone-flags) diff --git a/libs/cdk/zone-less/README.md b/libs/cdk/zone-less/README.md index e3f2f0dda9..5423a587b4 100644 --- a/libs/cdk/zone-less/README.md +++ b/libs/cdk/zone-less/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A set of wrappers for Browser native as well as RxJS functions to avoid unnessecary change detection and zone interference in Angular. +> A set of wrappers for Browser native as well as RxJS functions to avoid unnessecary change detection and zone interference in Angular. `@rx-angular/cdk/coalescing` is designed to help developers improve performance by not using zone.js patched API's. Besides a well documented and typed API it provides way to use patched API's in a way that is independent of `ngZone runOutsideZone` usage. @@ -31,4 +31,4 @@ yarn add @rx-angular/cdk ## Documentation -- [Zone-Less](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/zone-less/docs) +- [Zone-less](https://rx-angular.io/docs/cdk/zone-less) diff --git a/libs/cdk/zone-less/browser/README.md b/libs/cdk/zone-less/browser/README.md index be801627e8..effd212c98 100644 --- a/libs/cdk/zone-less/browser/README.md +++ b/libs/cdk/zone-less/browser/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A set of wrappers for Browser native functions to avoid unnessecary change detection and zone interference in Angular. +> A set of wrappers for Browser native functions to avoid unnessecary change detection and zone interference in Angular. `@rx-angular/cdk/zone-less/browse` is designed to help developers improve performance by avoiding the use of zone.js patched API's. Besides a well documented and typed API it provides way to use patched API's in a way that is independent of `ngZone runOutsideZone` usage. @@ -28,4 +28,4 @@ yarn add @rx-angular/cdk ## Documentation -- [Zone-Less](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/zone-less/docs) +- [Zone-Less](https://rx-angular.io/docs/cdk/zone-less) diff --git a/libs/cdk/zone-less/rxjs/README.md b/libs/cdk/zone-less/rxjs/README.md index acb99c3687..88d3d5887c 100644 --- a/libs/cdk/zone-less/rxjs/README.md +++ b/libs/cdk/zone-less/rxjs/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## A set of wrappers for RxJS to avoid unnessecary change detection and zone interference in Angular. +> A set of wrappers for RxJS to avoid unnessecary change detection and zone interference in Angular. `@rx-angular/cdk/zone-less/rxjs` is designed to help developers improve performance by avoiding the use of zone.js patched API's. Besides a well documented and typed API it provides way to use patched API's in a way that is independent of `ngZone runOutsideZone` usage. @@ -30,4 +30,4 @@ yarn add @rx-angular/cdk ## Documentation -- [Zone-Less](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/zone-less/docs) +- [Zone-less](https://rx-angular.io/docs/cdk/zone-less) diff --git a/libs/eslint-plugin/README.md b/libs/eslint-plugin/README.md index 1b47d05296..4f2eaeb463 100644 --- a/libs/eslint-plugin/README.md +++ b/libs/eslint-plugin/README.md @@ -42,8 +42,8 @@ Alternatively, if you prefer a more manual approach, add the plugin to your ESLi This plugin has two pre-defined configurations for different scenarios: - `@rx-angular/recommended` is recommended for most Angular applications, -- `@rx-angular/zoneless` is a stricter configuration for applications that aspire to [avoid triggering Zone.js](https://github.com/rx-angular/rx-angular/tree/main/libs/cdk/zone-less/docs). +- `@rx-angular/zoneless` is a stricter configuration for applications that aspire to [avoid triggering Zone.js](https://rx-angular.io/docs/cdk/zone-less). ## Rules -Documentation for individual rules may be found [here](https://github.com/rx-angular/rx-angular/tree/main/libs/eslint-plugin/docs/rules/). +Documentation for individual rules may be found [here](https://rx-angular.io/docs/eslint-plugin/rules). diff --git a/libs/eslint-plugin/src/lib/utils/docs.ts b/libs/eslint-plugin/src/lib/utils/docs.ts index 402b30525b..7e21e03252 100644 --- a/libs/eslint-plugin/src/lib/utils/docs.ts +++ b/libs/eslint-plugin/src/lib/utils/docs.ts @@ -1,3 +1,3 @@ export function docsUrl(ruleName: string): string { - return `https://github.com/rx-angular/rx-angular/blob/master/libs/eslint-plugin/docs/rules/${ruleName}.md`; + return `https://rx-angular.io/docs/eslint-plugin/rules/${ruleName}.md`; } diff --git a/libs/state/README.md b/libs/state/README.md index 06c09b4bbd..3a3599a4f1 100644 --- a/libs/state/README.md +++ b/libs/state/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/state/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/state/lcov-report/index.html) -## Reactive Component State for Angular +> Reactive Component State for Angular RxState is a lightweight, flexible, strongly typed and tested tool dedicated to reduce the complexity of managing component state in Angular. @@ -12,14 +12,13 @@ RxState is a lightweight, flexible, strongly typed and tested tool dedicated to ## Sub Modules -- [🧩 Selections](https://github.com/rx-angular/rx-angular/blob/main/libs/state/selections/README.md) -- [☁ Effects](https://github.com/rx-angular/rx-angular/blob/main/libs/state/effects/README.md) -- [? Actions](https://github.com/rx-angular/rx-angular/blob/main/libs/state/actions/README.md) +- [🧩 Selections](https://rx-angular.io/docs/state/selections) +- [☁ Effects](https://rx-angular.io/docs/state/effects) +- [? Actions](https://rx-angular.io/docs/state/actions) ## Intro Video -![intro-video_rx-angular--state-rx-state](https://user-images.githubusercontent.com/10064416/147395467-876ec499-645f-4f84-bde9-9bffaac22c62.PNG) - +[![intro-video_rx-angular--state-rx-state](https://user-images.githubusercontent.com/10064416/147395467-876ec499-645f-4f84-bde9-9bffaac22c62.PNG)](https://www.youtube.com/watch?v=CcQYj4V2IKw) ## Description @@ -85,42 +84,42 @@ nx migrate @rx-angular/state ## Usage -[Usage Documentation](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/usage.md) +[Usage Documentation](https://rx-angular.io/docs/state/setup) ## Testing -[Testing](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/testing.md) +[Testing](https://rx-angular.io/docs/state/testing) ## API -[API Documentation](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/api/overview.md) +[API Documentation](https://rx-angular.io/docs/state/api) ## Tutorials -- [Basic Tutorial](https://github.com/rx-angular/rx-angular/tree/main/apps/demos/src/app/features/tutorials/basics) +- [Basic Tutorial](https://rx-angular.io/docs/state/tutorials/basics) - [Counter - StackBlitz](https://stackblitz.com/edit/rx-angular-state-counter-demo?file=src%2Fapp%2Fcounter%2Fcounter.component.ts) ## Snippets -- [Logic comparison - Increment a Value](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/logic-comparison--increment-a-value.md) -- [Loading state and data fetching](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/loading-state-and-data-fetching.md) -- [Passing Observables directly](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/passing-observables-directly.md) -- [How to run partial state updates](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/how-can-i-run-partial-state-updates.md) -- [Get nested state slices](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/get-nested-state-slices.md) -- [Deriving simple state](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/deriving-simple-state.md) -- [Composing state using NgRx selectors](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/composing-state-using-ngrx-selectors.md) -- [Manage entities using NgRx entity adapter](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/manage-collections-with-ngrx-entity.md) -- [BehaviorSubject vs RxState](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/behavior-subject-vs-rx-state.md) -- [Managing ViewModels with selectSlice](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/selecting-the-viewmodel.md) -- [Manage reactive HostBindings](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/hostbindings.md) -- [Difference between Global and Local state](https://github.com/rx-angular/rx-angular/tree/main/libs/state/docs/snippets/global-state-vs-local-state.md) -- [Using RxState as Global State](https://github.com/rx-angular/rx-angular/blob/main/libs/state/docs/snippets/manage-global-state.md) +- [Logic comparison - Increment a Value](https://rx-angular.io/docs/state/tutorials/increment-a-value) +- [Loading state and data fetching](https://rx-angular.io/docs/state/recipes/load-data-on-route-change) +- [Passing Observables directly](https://rx-angular.io/docs/state/tutorials/passing-observables) +- [How to run partial state updates](https://rx-angular.io/docs/state/recipes/run-partial-updates) +- [Get nested state slices](https://rx-angular.io/docs/state/concepts-and-best-practices/get-nested-state-slices) +- [Deriving simple state](https://rx-angular.io/docs/state/concepts-and-best-practices/deriving-simple-state) +- [Composing state using NgRx selectors](https://rx-angular.io/docs/state/integrations/resuse-ngrx-selectors-to-compose-state) +- [Manage entities using NgRx entity adapter](https://rx-angular.io/docs/manage-entities-using-ngrx-entity) +- [BehaviorSubject vs RxState](https://rx-angular.io/docs/state/tutorials/migrating-to-rxstate) +- [Managing ViewModels with selectSlice](https://rx-angular.io/docs/state/recipes/manage-viewmodel) +- [Manage reactive HostBindings](https://rx-angular.io/docs/state/recipes/work-with-hostbindings) +- [Difference between Global and Local state](https://rx-angular.io/docs/state/recipes/determine-state-type) +- [Using RxState as Global State](https://rx-angular.io/docs/state/recipes/use-rxstate-as-global-state) ## Videos -![intro-video_rx-angular--state-rx-state](https://user-images.githubusercontent.com/10064416/147395467-876ec499-645f-4f84-bde9-9bffaac22c62.PNG)_🎥 RxAngular State, The Component Reactive Store | Marmicode Tasting Session_ +[![intro-video_rx-angular--state-rx-state](https://user-images.githubusercontent.com/10064416/147395467-876ec499-645f-4f84-bde9-9bffaac22c62.PNG)_🎥 RxAngular State, The Component Reactive Store | Marmicode Tasting Session_](https://www.youtube.com/watch?v=CcQYj4V2IKw) -![tackling-component-state-reactively](https://user-images.githubusercontent.com/10064416/147395866-031704dc-837d-4d1f-82d6-e758e4cb9556.PNG)_🎥 Tackling Component State Reactively (Live Demo at 24:47)_ +[![tackling-component-state-reactively](https://user-images.githubusercontent.com/10064416/147395866-031704dc-837d-4d1f-82d6-e758e4cb9556.PNG)_🎥 Tackling Component State Reactively (Live Demo at 24:47)_](https://www.youtube.com/watch?v=I8uaHMs8rw0) - [🎥 Extending Angular for the Reactive Web](https://youtu.be/pkN6CeZ8h_U?t=5913) @@ -141,11 +140,11 @@ nx migrate @rx-angular/state ## Version Compatibility | Angular | RxJS | @rx-angular/state | -|------------------------|----------------------|-------------------| +| ---------------------- | -------------------- | ----------------- | | `14` | `^7.4.0` | `> 1.4.6` | | `^12.0.0` or `^13.0.0` | `^6.5.5` or `^7.4.0` | `> 1.4.6` | | `^11.0.0` | `^6.5.5` | `<= 1.4.6` | Regarding the compatibility to RxJs, we generally stick to the compatibilities of the angular framework itself. All the packages support RxJs versions `^6.5.5` || `^7.4.0`. -For more information about the compatibilities of angular itself see this [gist](https://gist.github.com/LayZeeDK/c822cc812f75bb07b7c55d07ba2719b3) +For more information about the compatibilities of angular itself see this [gist](https://gist.github.com/LayZeeDK/c822cc812f75bb07b7c55d07ba2719b3) diff --git a/libs/state/actions/README.md b/libs/state/actions/README.md index 5836aaff64..320765056d 100644 --- a/libs/state/actions/README.md +++ b/libs/state/actions/README.md @@ -4,16 +4,15 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=master) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/state/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/state/lcov-report/index.html) -## This package provides a quick way to create an action channel and some configuration options. - +> This package provides a quick way to create an action channel and some configuration options. ## Key features - - ✅ Fully Typed - - ✅ No-Boilerplate - - ✅ Configurable transformations to have lines in the template - - ✅ Minimal memory footprint through a Proxy object and lazy initialization - +- ✅ Fully Typed +- ✅ No-Boilerplate +- ✅ Configurable transformations to have lines in the template +- ✅ Minimal memory footprint through a Proxy object and lazy initialization + ## Demos: - [⚡ GitHub](https://github.com/BioPhoton/rx-angular-state-actions) @@ -24,3 +23,4 @@ npm install --save @rx-angular/state # or yarn add @rx-angular/state +``` diff --git a/libs/state/docs/reactive-terminology.md b/libs/state/docs/reactive-terminology.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libs/state/effects/README.md b/libs/state/effects/README.md index 44e0fd703c..44ccf05d6f 100644 --- a/libs/state/effects/README.md +++ b/libs/state/effects/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=master) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/state/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/state/lcov-report/index.html) -## A small typed convenience helper to handle side effects and Observable subscriptions. +> A small typed convenience helper to handle side effects and Observable subscriptions. `@rx-angular/state/effects` is a small set of helpers designed to handle effects. @@ -14,7 +14,7 @@ - ✅ Easy to test - ✅ Clean separation of concerns - ✅ Slim and handy APIs -- ✅ Auto-cleanup on destroy +- ✅ Auto-cleanup on destroy - ✅ Effect interoperability - ✅ Handlers for imperative code styles @@ -22,7 +22,6 @@ - [⚡ GitHub](https://github.com/rx-angular/rx-angular/tree/main/apps/demos/src/app/features/tutorials/basics/5-side-effects) - ## Install ```bash @@ -45,4 +44,3 @@ nx migrate @rx-angular/state ## Documentation - [RxEffects](https://github.com/rx-angular/rx-angular/tree/main/libs/state/effects/docs) - diff --git a/libs/state/selections/README.md b/libs/state/selections/README.md index 2c91948026..f77c1f745a 100644 --- a/libs/state/selections/README.md +++ b/libs/state/selections/README.md @@ -4,13 +4,13 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=master) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/cdk/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/cdk/lcov-report/index.html) -## Slogan +> Slogan `@rx-angular/state/selections` TBD ## Key features -- ✅ +- ✅ ## Demos: @@ -26,5 +26,4 @@ yarn add @rx-angular/state ## Documentation -- [Selections](https://github.com/rx-angular/rx-angular/tree/main/libs/state/selections/docs/Readme.md) - +- [Selections](https://rx-angular.io/docs/state/selections) diff --git a/libs/state/selections/docs/Readme.md b/libs/state/selections/docs/Readme.md deleted file mode 100644 index 648364e263..0000000000 --- a/libs/state/selections/docs/Readme.md +++ /dev/null @@ -1,10 +0,0 @@ -# Motivation - -TBD - -* [`Interfaces`](./operators/interfaces.md) -* [`distinctUntilSomeChanged`](./operators/distinct-until-some-changed.md) -* [`select`](./operators/select.md) -* [`selectSlice`](./operators/select-slice.md) -* [`stateful`](./operators/stateful.md) - diff --git a/libs/template/README.md b/libs/template/README.md index 94172410f7..491c52fdfd 100644 --- a/libs/template/README.md +++ b/libs/template/README.md @@ -4,7 +4,7 @@ ![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=main) [![Coverage Status](https://raw.githubusercontent.com/rx-angular/rx-angular/github-pages/docs/test-coverage/template/jest-coverage-badge.svg)](https://rx-angular.github.io/rx-angular/test-coverage/template/lcov-report/index.html) -## Reactive Template Rendering for Angular +> Reactive Template Rendering for Angular @rx-angular/template is a comprehensive toolset for fully reactive rendering in Angular. It leverages the latest Browser APIs (while still being backward compatible) to maximize the rendering performance and thus @@ -19,20 +19,20 @@ structural directives, pipes, RxJS operators, or imperative functions to manage **@rx-angular/template** is nothing less than a revolution in `ChangeDetection` for angular applications. Developers are provided with tools for high-performance rendering, which are operated by a broad and intuitive API. -The [LetDirective (`*rxLet`)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/let-directive.md) & -[PushPipe (`push`)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/push-pipe.md) focus +The [LetDirective (`*rxLet`)](https://rx-angular.io/docs/template/api/let-directive) & +[PushPipe (`push`)](https://rx-angular.io/docs/template/api/push-pipe) focus on template rendering, the coordination and optimization of `ChangeDetection` cycles. While the `PushPipe` is a straight **drop-in replacement** for the `AsyncPipe (async)`, the `LetDirective` will often provide a more convenient way of managing reactive sources and lazy rendering of the view. -Should be noted that both [LetDirective (`*rxLet`)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/let-directive.md) & -[PushPipe (`push`)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/push.md) recognize only immutable changes. +Should be noted that both [LetDirective (`*rxLet`)](https://rx-angular.io/docs/template/api/let-directive) & +[PushPipe (`push`)](https://rx-angular.io/docs/template/api/push-pipe) recognize only immutable changes. -Using those with the default strategy ([Local Strategy](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/docs/render-strategies/strategies.md#local)) should already improve the rendering performance of +Using those with the default strategy ([Local Strategy](http://rx-angular.io/docs/cdk/render-strategies/basic-strategies#local)) should already improve the rendering performance of your application by a decent amount. The applied optimization behavior is fully customizable by using built-in or -custom-provided [RenderStrategies](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/render-strategies/docs/README.md). +custom-provided [RenderStrategies](https://rx-angular.io/docs/cdk/render-strategies). However, `RenderStrategies` are also meant to be a tool developers can interact with inside their components, giving you even broader access to the rendering mechanisms of your application. The API comes with imperative as well as reactive ways to manage renderings. @@ -41,8 +41,8 @@ Beyond the optimization of change detection cycles, `@rx-angular/template` by de If you want to deepen your knowledge about performance optimizations, consider reading through the following concepts and techniques: -- [Coalescing, Scoped Coalescing & Scheduling](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/concepts.md) -- [Rendering Issues in Angular](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/performance-issues.md) +- [Coalescing, Scoped Coalescing & Scheduling](https://rx-angular.io/docs/template/concepts) +- [Rendering Issues in Angular](https://rx-angular.io/docs/template/concepts/performance-issues) ## Installation @@ -75,7 +75,7 @@ nx migrate @rx-angular/template ## API -[API Documentation](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/overview.md) +[API Documentation](https://rx-angular.io/docs/template/api) ## Basic setup @@ -96,29 +96,28 @@ export class MyModule {} ## Features - Directives - - [LetDirective (\*rxLet)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/let-directive.md) - - [UnpatchDirective (unpatch)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/unpatch-directive.md) + - [LetDirective (\*rxLet)](https://rx-angular.io/docs/template/api/let-directive) + - [RxFor (\*rxFor)](https://rx-angular.io/docs/template/api/rx-for-directive) + - [UnpatchDirective (unpatch)](https://rx-angular.io/docs/template/unpatch-directive) - Pipes - - [PushPipe (push)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/api/push-pipe.md) + - [PushPipe (push)](https://rx-angular.io/docs/template/api/push-pipe) ## Experimental features Additionally, `@rx-angular/template` provides some experimental optimization tools which in general will give you more control about what changes are leading to re-renderings. -- [🧪 RxIf (\*rxIf)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/experimental/rx-if-directive.md) -- [🧪 RxFor (\*rxFor)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/experimental/rx-for-directive.md) -- [🧪 Viewport Priority (viewport-prio)](https://github.com/rx-angular/rx-angular/tree/main/libs/template/docs/experimental/viewport-prio.md) +- [🧪 RxIf (\*rxIf)](https://rx-angular.io/docs/template/api/experimental/rx-if-directive) +- [🧪 Viewport Priority (viewport-prio)](https://rx-angular.io/docs/template/api/experimental/viewport-prio-directive) ## Version Compatibility | Angular | RxJS | @rx-angular/template | -|------------------------|----------------------|----------------------| +| ---------------------- | -------------------- | -------------------- | | `14` | `^7.4.0` | `> 1.0.0-beta.29` | | `^12.0.0` or `^13.0.0` | `^6.5.5` or `^7.4.0` | `> 1.0.0-beta.29` | | `^11.0.0` | `^6.5.5` | `<= 1.0.0-beta.29` | - Regarding the compatibility to RxJs, we generally stick to the compatibilities of the angular framework itself. All the packages support RxJs versions `^6.5.5` || `^7.4.0`. For more information about the compatibilities of angular itself see this [gist](https://gist.github.com/LayZeeDK/c822cc812f75bb07b7c55d07ba2719b3) diff --git a/libs/template/docs/api/overview.md b/libs/template/docs/api/overview.md deleted file mode 100644 index 09e5bce86d..0000000000 --- a/libs/template/docs/api/overview.md +++ /dev/null @@ -1,17 +0,0 @@ -## API - -### Directives - -- [`LetDirective`](./let-directive.md) -- [`UnpatchDirective`](./unpatch-directive.md) - -#### Experimental - -- [🧪 `RxFor`](./rx-for-directive.md) -- [🧪 `RxIf`](./rx-if-directive.md) -- [🧪 `ViewportPriority`](./viewport-prio.md) - -### Pipes - -- [`PushPipe`](./push-pipe.md) - diff --git a/libs/template/push/src/lib/push.pipe.ts b/libs/template/push/src/lib/push.pipe.ts index f1521ab08e..b9d9a9e4a2 100644 --- a/libs/template/push/src/lib/push.pipe.ts +++ b/libs/template/push/src/lib/push.pipe.ts @@ -40,7 +40,7 @@ import { * * The push pipe serves as a drop-in replacement for angulars built-in async pipe. * Just like the *rxLet Directive, it leverages a - * [RenderStrategy](https://github.com/rx-angular/rx-angular/blob/main/libs/cdk/docs/render-strategies/README.md) + * [RenderStrategy](https://rx-angular.io/docs/cdk/render-strategies) * under the hood which takes care of optimizing the ChangeDetection of your component. The rendering behavior can be * configured per PushPipe instance using either a strategy name or provide a * `RxComponentInput` config. diff --git a/nx.json b/nx.json index 537a64eb32..5b9aa36a10 100644 --- a/nx.json +++ b/nx.json @@ -8,7 +8,23 @@ }, "tsconfig.json": "*", "nx.json": "*", - ".eslintrc.json": "*" + ".eslintrc.json": "*", + "CONTRIBUTING.md": ["docs"], + "libs/cdk/README.md": ["docs"], + "libs/cdk/coalescing/README.md": ["docs"], + "libs/cdk/coercing/README.md": ["docs"], + "libs/cdk/notifications/README.md": ["docs"], + "libs/cdk/render-strategies/README.md": ["docs"], + "libs/cdk/transformations/README.md": ["docs"], + "libs/cdk/zone-configurations/README.md": ["docs"], + "libs/cdk/zone-less/README.md": ["docs"], + "libs/cdk/template/README.md": ["docs"], + "libs/eslint-plugin/README.md": ["docs"], + "libs/state/README.md": ["docs"], + "libs/state/actions/README.md": ["docs"], + "libs/state/effects/README.md": ["docs"], + "libs/state/selections/README.md": ["docs"], + "libs/template/README.md": ["docs"] }, "tasksRunnerOptions": { "default": { diff --git a/tsconfig.base.json b/tsconfig.base.json index ee33ba0e91..d248a85fec 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -48,19 +48,17 @@ ], "@rx-angular/eslint-plugin": ["libs/eslint-plugin/src/index.ts"], "@rx-angular/state": ["libs/state/src/index.ts"], + "@rx-angular/state/actions": ["libs/state/actions/src/index.ts"], "@rx-angular/state/effects": ["libs/state/effects/src/index.ts"], "@rx-angular/state/selections": ["libs/state/selections/src/index.ts"], - "@rx-angular/state/actions": ["libs/state/actions/src/index.ts"], "@rx-angular/template": ["libs/template/src/index.ts"], - "@rx-angular/template/for": [ - "libs/template/for/src/index.ts" - ], "@rx-angular/template/experimental/if": [ "libs/template/experimental/if/src/index.ts" ], "@rx-angular/template/experimental/viewport-prio": [ "libs/template/experimental/viewport-prio/src/index.ts" ], + "@rx-angular/template/for": ["libs/template/for/src/index.ts"], "@rx-angular/template/let": ["libs/template/let/src/index.ts"], "@rx-angular/template/push": ["libs/template/push/src/index.ts"], "@rx-angular/template/unpatch": ["libs/template/unpatch/src/index.ts"],