Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f190540

Browse files
committed
docs: add selection docs
1 parent 1d6c77f commit f190540

File tree

1 file changed

+253
-21
lines changed

1 file changed

+253
-21
lines changed

apps/docs/docs/state/selections/selections.mdx

Lines changed: 253 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,269 @@ hide_title: true
77

88
# @rx-angular/state/selections
99

10-
[![npm](https://img.shields.io/npm/v/%40rx-angular%2Fcdk.svg)](https://www.npmjs.com/package/%40rx-angular%2Fcdk)
11-
![rx-angular CI](https://github.com/rx-angular/rx-angular/workflows/rx-angular%20CI/badge.svg?branch=master)
10+
## Motivation
11+
12+
![Selections (1)](https://user-images.githubusercontent.com/10064416/152422745-b3d8e094-d0f0-4810-b1b2-5f81fae25938.png)
1213

13-
> This package provides a set of RxJS operators to select the state in an efficient way.
14+
When managing state you want to maintain a core unit of data.
15+
This data is then later on distributed to multiple places in your component template (local) or whole app (global).
1416

15-
## Key features
17+
We can forward this state to their consumers directly or compute specific derivations (selections) for the core unit.
1618

17-
-
19+
As an example we could think of the following shape:
1820

19-
## Demos:
21+
**A list and a list title**
2022

21-
- ⚡ GitHub
23+
```typescript
24+
interface GlobalModel {
25+
title: string;
26+
list: Array<{ id: number; date: Date }>;
27+
}
28+
```
2229

23-
## Install
30+
This data is consumed in different screens:
2431

25-
```bash
26-
npm install --save @rx-angular/state
27-
# or
28-
yarn add @rx-angular/state
32+
**A list of all items sorted by id**
33+
34+
```typescript
35+
interface SelectionScreen1 {
36+
title: string;
37+
sortDirection: 'asc' | 'desc' | 'none';
38+
sortedList: Array<{ id: number }>;
39+
}
2940
```
3041

31-
## Documentation
42+
**A list of items filtered by date**
3243

33-
- [Selections](https://rx-angular.io/docs/state/selections)
44+
```typescript
45+
interface SelectionScreen2 {
46+
title: string;
47+
startingDate: Date;
48+
filteredList: { id: number };
49+
}
50+
```
3451

35-
## Motivation
52+
The 2 rendered lists are a derivation, a modified version of the core set of items.
53+
One time they are displayed in a sorted order, the other time only filtered subset of the items.
54+
55+
> **Hint:**
56+
> Derivations are always redundant information of our core data and therefore should not get stored,
57+
> but cached in the derivation logic.
58+
59+
![Selections (2)](https://user-images.githubusercontent.com/10064416/152422803-bfd07ab2-0a6f-4521-836e-b71677e11923.png)
60+
61+
As this process contains a lot of gotchas and possible pitfalls in terms of memory usage and performance this small helper library was created.
62+
63+
# Benefits
64+
65+
![Selections (3)](https://user-images.githubusercontent.com/10064416/152422856-a483a06c-84e0-4067-9eaa-f3bb54a0156d.png)
66+
67+
- Sophisticated set of helpers for any selection problem
68+
- Enables lazy rendering
69+
- Computes only distinct values
70+
- Shares computed result with multiple subscriber
71+
- Select distinct sub-sets
72+
- Select from static values
73+
- Fully tested
74+
- Strongly typed
75+
76+
![Selections (5)](https://user-images.githubusercontent.com/10064416/152422955-cb89d198-1a69-450b-be84-29dd6c8c4fdb.png)
77+
78+
In most cases it's best to go with solving problems on the early subscriber side and be sure we never loose values that should render on the screen.
79+
80+
![Selections (4)](https://user-images.githubusercontent.com/10064416/152422883-0b5f6006-7929-4520-b0b2-79eb61e4eb08.png)
81+
82+
# Usage
83+
84+
## select
85+
86+
`select` is the stand-alone version of the `RxState#select` top level method. It helps to create default selection's from a changing state source.
87+
88+
```typescritp
89+
// emissions:
90+
// 0. - no emission ever happened
91+
// 1. {a: 1} - incomplete state leads to `?` pollution in the template
92+
// 2. {a: 1, b: 'a'} - render relevant emission
93+
// 2. {a: 1, b: 'a'} - same instance emisssion
94+
// 3. {a: 1, b: 'a', c: true} - render irrelevant change
95+
// 4. {a: 1, b: 'b', c: true} - render relevant emission
96+
const model$: Observable<Partial<{a: number, b: string, c: boolean}>>;
97+
```
98+
99+
**Problem**
100+
101+
```html
102+
<!--
103+
104+
Computes 2 times & Renders 0. ❌; 1. ❌; 2. ✅; 3. ❌; .4 ✅
105+
-->
106+
<div *rxLet="model$; let vm">B: {{vm?.b}}</div>
107+
B: {{(model$ | push)?.b}}
108+
```
109+
110+
### single property short hand
111+
112+
```typescritp
113+
const vm$ = model$.pipe(select('b'));
114+
```
115+
116+
```html
117+
<!--
118+
Computes 1 time & Renders 2. ✅; .4 ✅
119+
-->
120+
<div *rxLet="model$; let vm">B: {{vm.b}}</div>
121+
B: {{(model$ | push).b}}
122+
```
123+
124+
### single operators
125+
126+
```typescritp
127+
const vm$: Observable<> = model$.pipe(select(map(({b}) => b === 'a')));
128+
```
129+
130+
```html
131+
<!--
132+
Computes 1 time & Renders 2. ✅; .4 ✅
133+
-->
134+
<div *rxLet="model$; let vm">B: {{vm.b}}</div>
135+
B: {{(model$ | push).b}}
136+
```
36137

37-
TBD
138+
## selectSlice
139+
140+
TODO
141+
142+
## distinctUntilSomeChanges
143+
144+
TODO
145+
146+
# Advanced derivation architecture
147+
148+
**The problem**
149+
150+
We have the following state sources to manage:
151+
152+
- the list of products received form global state - `Product[]`
153+
- the title of the list including it's number of children computen in the component class - `string`
154+
- the sort direction triggered over a UI element click - `boolean`
155+
156+
A setup of the compoents class based on `RxState` could look like this:
157+
158+
```typescript
159+
@Component({
160+
selector: 'app-problem',
161+
template: `
162+
<ng-container *rxLet="viewModel$; let vm">
163+
<h1>{{vm.title}} - {{vm.sortDirection}}</h1>
164+
<ul>
165+
<li *ngFor="let item of vm.sortedList">{{item}}</li>
166+
</ul>
167+
</ng-container>
168+
`,
169+
providers: [RxState],
170+
})
171+
export class ProblemComponent {
172+
173+
viewModel$: Observable<ViewModel>; // ???
174+
175+
constructor(private globalState: GlobalState, private state: RxState<Model>) {
176+
this.state.connect('title', this.globalState.title$);
177+
this.state.connect('products', this.globalState.products$);
178+
}
179+
180+
toggleSort() {
181+
this.state.set('sort', ({sort}) => !sort))
182+
}
183+
}
184+
185+
```
186+
187+
In a components template we want to render the the UI for the above explained view model `SelectionScreen1`.
188+
189+
```typescript
190+
interface SelectionScreen1 {
191+
title: string;
192+
sortDirection: 'asc' | 'desc' | 'none';
193+
sortedList: Array<{ id: number }>;
194+
}
195+
```
196+
197+
A common implementations looks like this:
198+
199+
```typescript
200+
// template removed for brevity
201+
export class ProblemComponent {
202+
private sortedList$ = this.state.select(
203+
selectSlice(['sortDirection', 'list']),
204+
map(() => {
205+
// sort `list` by `sortDirection` to `sortedList` here
206+
return sortedList;
207+
})
208+
);
209+
210+
viewModel$ = this.state.select(
211+
selectSlice(['title', 'sortedList', 'sortDirection'])
212+
);
213+
214+
// ❌ BAD: modle viewmodel mix up 👇
215+
constructor(
216+
private globalState: GlobalState,
217+
private state: RxState<Model & Pick<ViewModel, 'sortedList'>>
218+
) {
219+
// ...
220+
221+
// ❌ BAD: store derived state 👇
222+
this.state.connect('sortedList', this.sortedList$);
223+
}
224+
225+
// ...
226+
}
227+
```
228+
229+
![Selections (6)](https://user-images.githubusercontent.com/10064416/152422999-db8260f0-69e1-4d99-b6ac-b2b1d043b4b7.png)
230+
231+
By removing the sorted list form the state and moving it into the selection
232+
we can clean up the state's typing and have a nice separation of which data is owned by the component (model) and which data is owned by the template (view model)
233+
234+
```typescript
235+
// template removed for brevity
236+
export class ProblemComponent {
237+
private sortedSlice$ = this.state.select(
238+
selectSlice(['sortDirection', 'list']),
239+
map(({ list, sortDirection }) => {
240+
// sort `list` by `sortDirection` to `sortedList` here
241+
return { sortDirection, sortedList };
242+
})
243+
);
244+
245+
// ✔ GOOD: Derive view model from model 👇
246+
viewModel$ = smosh({ title: this.state.select('title') }, this.sortedSlice$);
247+
248+
// target API
249+
viewModel$ = smosh(
250+
{
251+
prop1: 'prop1', // string
252+
prop2: prop1$, // Observable<string>
253+
},
254+
slice1$, // Observable<{prop3: 3}>
255+
slice2$ // Observable<{prop4: 'four'}>,
256+
// durationSelector$ (optional)
257+
);
258+
259+
// ✔ GOOD: Derive view model from model 👇
260+
viewModel$ = smosh(
261+
{
262+
title: this.state.select('title'),
263+
},
264+
[this.sortedSlice$]
265+
);
266+
267+
constructor(private globalState: GlobalState, private state: RxState<Model>) {
268+
// ...
269+
}
270+
271+
// ...
272+
}
273+
```
38274

39-
- [`Interfaces`](api/interfaces/)
40-
- [`distinctUntilSomeChanged`](../api/rxjs-operators/distinct-until-some-changed.md)
41-
- [`select`](../api/rxjs-operators/select.md)
42-
- [`selectSlice`](../api/rxjs-operators/select-slice.md)
43-
- [`stateful`](../api/rxjs-operators/stateful.md)
275+
![Selections (7)](https://user-images.githubusercontent.com/10064416/152423026-d23326c2-97d5-4bd0-9015-f498c3fc0e55.png)

0 commit comments

Comments
 (0)