From 765777ff054355652d29eb6c3258cba5d82f6e34 Mon Sep 17 00:00:00 2001 From: Enea Jahollari Date: Wed, 13 Sep 2023 20:46:34 +0200 Subject: [PATCH] docs: added computedFrom docs (#30) * docs: added computedFrom docs * docs: added computedFrom docs * docs: added logo * docs: added logo in index --- docs/astro.config.mjs | 16 ++ docs/public/logo.svg | 1 + docs/src/content/docs/index.mdx | 4 + .../content/docs/utilities/computed-from.md | 206 ++++++++++++++++++ docs/src/content/docs/utilities/connect.md | 8 + .../content/docs/utilities/inject-destroy.md | 8 + 6 files changed, 243 insertions(+) create mode 100644 docs/public/logo.svg create mode 100644 docs/src/content/docs/utilities/computed-from.md create mode 100644 docs/src/content/docs/utilities/connect.md create mode 100644 docs/src/content/docs/utilities/inject-destroy.md diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 1f6d53bb..be34311d 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -6,6 +6,10 @@ export default defineConfig({ integrations: [ starlight({ title: 'ngxtension', + logo: { + src: './public/logo.svg', + alt: 'ngxtension logo', + }, social: { github: 'https://github.com/nartc/ngxtension-platform', twitter: 'https://twitter.com/Nartc1410', @@ -29,6 +33,18 @@ export default defineConfig({ label: 'createInjectionToken', link: '/utilities/create-injection-token', }, + { + label: 'computedFrom', + link: '/utilities/computed-from', + }, + { + label: 'injectDestroy', + link: '/utilities/inject-destroy', + }, + { + label: 'connect', + link: '/utilities/connect', + }, { label: 'repeat', link: '/utilities/repeat' }, { label: 'resize', link: '/utilities/resize' }, ], diff --git a/docs/public/logo.svg b/docs/public/logo.svg new file mode 100644 index 00000000..26bad100 --- /dev/null +++ b/docs/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index b44e0eca..b4cb497d 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -2,6 +2,10 @@ title: Welcome to ngxtension template: splash hero: + image: + file: ../../../public/logo.svg + alt: ngxtension logo + actions: - text: Getting Started link: /getting-started/introduction diff --git a/docs/src/content/docs/utilities/computed-from.md b/docs/src/content/docs/utilities/computed-from.md new file mode 100644 index 00000000..a91ec77b --- /dev/null +++ b/docs/src/content/docs/utilities/computed-from.md @@ -0,0 +1,206 @@ +--- +title: computedFrom +description: ngxtension/computed-from +--- + +`computedFrom` is a helper function that combines the values of `Observable`s or `Signal`s and emits the combined value. +It also gives us the possibility to change the combined value before emitting it using rxjs operators. + +It is similar to `combineLatest`, but it also takes `Signals` into consideration. + +```ts +import { computedFrom } from 'ngxtension/computed-from'; +``` + +:::tip[Inside story of the function] +Read more here: [A sweet spot between signals and observables 🍬](https://itnext.io/a-sweet-spot-between-signals-and-observables-a3c9620768f1) +::: + +## Usage + +`computedFrom` accepts an array or object of `Observable`s or `Signal`s and returns a `Signal` that emits the combined value of the `Observable`s or `Signal`s. +By default, it needs to be called in an injection context, but it can also be called outside of it by passing the `Injector` as the third argument. + +```ts +const a = signal(1); +const b$ = new BehaviorSubject(2); + +// array type +const combined = computedFrom([a, b$]); +console.log(combined()); // [1, 2] + +// object type +const combined = computedFrom({ a, b: b$ }); +console.log(combined()); // { a: 1, b: 2 } +``` + +It can be used in multiple ways: + +1. Combine multiple `Signal`s +2. Combine multiple `Observable`s +3. Combine multiple `Signal`s and `Observable`s +4. Use it outside of an injection context + +### 1. Combine multiple `Signal`s + +We can use `computedFrom` to combine multiple `Signal`s into one `Signal`, which will emit the combined value of the `Signal`s. + +```ts +const page = signal(1); +const filters = signal({ name: 'John' }); + +const combined = computedFrom([page, filters]); + +console.log(combined()); // [1, { name: 'John' }] +``` + +At this point we still don't get any benefit from using `computedFrom` because we can already combine multiple `Signal`s using `computed` function. +But, what's better is that `computedFrom` allows us to change the combined value before emitting it using rxjs operators (applying asynchronous operations), which is not possible with `computed`. + +```ts +import { computedFrom } from 'ngxtension/computed-from'; +import { delay, of, pipe, switchMap } from 'rxjs'; + +let a = signal(1); +let b = signal(2); + +let c = computedFrom( + [a, b], + pipe( + switchMap( + ([a, b]) => + // of(a + b) is supposed to be an asynchronous operation (e.g. http request) + of(a + b).pipe(delay(1000)) // delay the emission of the combined value by 1 second for demonstration purposes + ) + ) +); + +effect(() => console.log(c())); + +setTimeout(() => { + a.set(3); +}, 3000); + +// You can copy the above example inside an Angular constructor and see the result in the console. +``` + +The console log will be: + +```ts +-[1, 2] - // initial value + 3 - // combined value after 1 second + 5; // combined value after 3 seconds +``` + +As we can see, the first value will not be affected by the rxjs operators, because they are asynchronous and the first value is emitted synchronously. +In order to change the first value, we can use startWith operator. + +```ts +let c = computedFrom( + [a, b], + pipe( + switchMap(([a, b]) => of(a + b).pipe(delay(1000))), + startWith(0) // change the first value + ) +); +``` + +The console log will be: + +```ts +-0 - // initial value + 3 - // combined value after 1 second + 5; // combined value after 3 seconds +``` + +### 2. Combine multiple `Observable`s + +We can use `computedFrom` to combine multiple `Observable`s into one `Signal`, which will emit the combined value of the `Observable`s. + +```ts +const page$ = new BehaviorSubject(1); +const filters$ = new BehaviorSubject({ name: 'John' }); + +const combined = computedFrom([page$, filters$]); + +console.log(combined()); // [1, { name: 'John' }] +``` + +This is just a better version of: + +```ts +const combined = toSignal(combineLatest([page$, filters$])); +``` + +And it can be used in the same way as in the previous example with rxjs operators. + +### 3. Combine multiple `Signal`s and `Observable`s + +This is where `computedFrom` shines. We can use it to combine multiple `Signal`s and `Observable`s into one `Signal`. + +```ts +const page = signal(1); +const filters$ = new BehaviorSubject({ name: 'John' }); + +const combined = computedFrom([page, filters$]); +console.log(combined()); // [1, { name: 'John' }] + +// or using the object notation +const combinedObject = computedFrom({ page, filters: filters$ }); +console.log(combinedObject()); // { page: 1, filters: { name: 'John' } } +``` + +:::note[Tricky part] +For `Observable`s that don't emit synchronously, `computedFrom` will give us null as the initial value for the `Observable`s. +::: + +```ts +const page$ = new Subject(); // Subject doesn't have an initial value +const filters$ = new BehaviorSubject({ name: 'John' }); +const combined = computedFrom([page$, filters$]); + +console.log(combined()); // [null, { name: 'John' }] +``` + +But, we can always use the `startWith` operator to change the initial value. + +```ts +const combined = computedFrom([ + page$.pipe(startWith(0)), // change the initial value to 0 + filters$, +]); + +console.log(combined()); // [0, { name: 'John' }] +``` + +### 4. Use it outside of an injection context + +By default, `computedFrom` needs to be called in an injection context, but it can also be called outside of it by passing the `Injector` as the third argument. + +```ts +@Component() +export class MyComponent { + private readonly injector = inject(Injector); + private readonly dataService = inject(DataService); + + // we can read the userId inside ngOnInit and not in the constructor + @Input() userId!: number; + + data!: Signal; + + ngOnInit() { + // not an injection context + const page = signal(1); + const filters$ = new BehaviorSubject({ name: 'John' }); + + this.data = computedFrom( + [page, filters$], + pipe( + switchMap(([page, filters]) => this.dataService.getUserData(this.userId, page, filters)), + startWith([] as string[]) // change the initial value + ), + this.injector // 👈 pass the injector as the third argument + ); + } +} +``` diff --git a/docs/src/content/docs/utilities/connect.md b/docs/src/content/docs/utilities/connect.md new file mode 100644 index 00000000..63d95ce7 --- /dev/null +++ b/docs/src/content/docs/utilities/connect.md @@ -0,0 +1,8 @@ +--- +title: connect +description: ngxtension/connect +--- + +WIP + +Link to [connect](https://github.com/nartc/ngxtension-platform/blob/main/libs/ngxtension/connect/src/connect.ts) source code. diff --git a/docs/src/content/docs/utilities/inject-destroy.md b/docs/src/content/docs/utilities/inject-destroy.md new file mode 100644 index 00000000..a72efed0 --- /dev/null +++ b/docs/src/content/docs/utilities/inject-destroy.md @@ -0,0 +1,8 @@ +--- +title: injectDestroy +description: ngxtension/inject-destroy +--- + +WIP + +Link to [injectDestroy](https://github.com/nartc/ngxtension-platform/blob/main/libs/ngxtension/inject-destroy/src/inject-destroy.ts) source code.