@rs-x/state-manager 0.4.11 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +52 -47
- package/dist/index.js +323 -300
- package/package.json +2 -2
- package/readme.md +605 -529
package/readme.md
CHANGED
|
@@ -7,9 +7,9 @@ A context can be an object, and an index can be a property name — but it is no
|
|
|
7
7
|
|
|
8
8
|
Examples of state indexes:
|
|
9
9
|
|
|
10
|
-
- Object property or field → index = property or field name
|
|
11
|
-
- Array item → index = numeric position
|
|
12
|
-
- Map item → index = map key
|
|
10
|
+
- Object property or field → index = property or field name
|
|
11
|
+
- Array item → index = numeric position
|
|
12
|
+
- Map item → index = map key
|
|
13
13
|
|
|
14
14
|
The State Manager does **not** automatically know how to detect changes for every state value data type, nor how to patch the corresponding state setter. Therefore, it relies on two services:
|
|
15
15
|
|
|
@@ -19,45 +19,38 @@ The State Manager does **not** automatically know how to detect changes for ever
|
|
|
19
19
|
- A service implementing `IIndexValueAccessor`
|
|
20
20
|
Responsible for retrieving the current value.
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
The **State Manager** has the followng interface:
|
|
23
|
+
|
|
24
24
|
```ts
|
|
25
25
|
export interface IStateManager {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setState<T>(
|
|
55
|
-
context: unknown,
|
|
56
|
-
index: unknown,
|
|
57
|
-
value: T
|
|
58
|
-
): void;
|
|
59
|
-
|
|
60
|
-
clear(): void;
|
|
26
|
+
readonly changed: Observable<IStateChange>;
|
|
27
|
+
readonly contextChanged: Observable<IContextChanged>;
|
|
28
|
+
readonly startChangeCycle: Observable<void>;
|
|
29
|
+
readonly endChangeCycle: Observable<void>;
|
|
30
|
+
|
|
31
|
+
isWatched(
|
|
32
|
+
context: unknown,
|
|
33
|
+
index: unknown,
|
|
34
|
+
mustProxify: MustProxify,
|
|
35
|
+
): boolean;
|
|
36
|
+
|
|
37
|
+
watchState(
|
|
38
|
+
context: unknown,
|
|
39
|
+
index: unknown,
|
|
40
|
+
mustProxify?: MustProxify,
|
|
41
|
+
): unknown;
|
|
42
|
+
|
|
43
|
+
releaseState(
|
|
44
|
+
oontext: unknown,
|
|
45
|
+
index: unknown,
|
|
46
|
+
mustProxify?: MustProxify,
|
|
47
|
+
): void;
|
|
48
|
+
|
|
49
|
+
getState<T>(context: unknown, index: unknown): T;
|
|
50
|
+
|
|
51
|
+
setState<T>(context: unknown, index: unknown, value: T): void;
|
|
52
|
+
|
|
53
|
+
clear(): void;
|
|
61
54
|
}
|
|
62
55
|
```
|
|
63
56
|
|
|
@@ -66,12 +59,14 @@ export interface IStateManager {
|
|
|
66
59
|
## Members
|
|
67
60
|
|
|
68
61
|
### **changed**
|
|
62
|
+
|
|
69
63
|
**Type:** `Observable<IStateChange>`
|
|
70
64
|
Emits whenever a state item value changes.
|
|
71
65
|
|
|
72
66
|
---
|
|
73
67
|
|
|
74
68
|
### **contextChanged**
|
|
69
|
+
|
|
75
70
|
**Type:** `Observable<IContextChanged>`
|
|
76
71
|
Emits whenever an entire context is replaced.
|
|
77
72
|
This happens, for example, when a nested object is replaced.
|
|
@@ -79,38 +74,42 @@ This happens, for example, when a nested object is replaced.
|
|
|
79
74
|
---
|
|
80
75
|
|
|
81
76
|
### **startChangeCycle**
|
|
77
|
+
|
|
82
78
|
**Type:** `Observable<void>`
|
|
83
79
|
Emits at the start of processing a state item change.
|
|
84
80
|
|
|
85
81
|
---
|
|
86
82
|
|
|
87
83
|
### **endChangeCycle**
|
|
84
|
+
|
|
88
85
|
**Type:** `Observable<void>`
|
|
89
86
|
Emits at the end of processing a state item change.
|
|
90
87
|
|
|
91
88
|
---
|
|
92
89
|
|
|
93
90
|
### **isWatched(context, index, mustProxify)**
|
|
91
|
+
|
|
94
92
|
Returns whether the state item identified by the `(context, index, mustProxify)` triplet is currently being watched.
|
|
95
93
|
|
|
96
|
-
| Parameter | Type | Description
|
|
97
|
-
| --------------- | -------------------------- |
|
|
98
|
-
| **context** | `unknown` | The context to which the state index belongs.
|
|
99
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
100
|
-
| **mustProxify** | `MustProxify`
|
|
94
|
+
| Parameter | Type | Description |
|
|
95
|
+
| --------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| **context** | `unknown` | The context to which the state index belongs. |
|
|
97
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
98
|
+
| **mustProxify** | `MustProxify` _(optional)_ | Predicate determining whether a nested state value must be proxified. It should be the same predicate that was passed to `watchState`. |
|
|
101
99
|
|
|
102
100
|
**Returns:** `boolean`
|
|
103
101
|
|
|
104
102
|
---
|
|
105
103
|
|
|
106
104
|
### **watchState(context, index, mustProxify?)**
|
|
105
|
+
|
|
107
106
|
Watches a state item identified by the `(context, index, mustProxify)` triplet so that its value is proxified and tracked.
|
|
108
107
|
|
|
109
|
-
| Parameter | Type | Description
|
|
110
|
-
| --------------- | -------------------------- |
|
|
111
|
-
| **context** | `unknown` | The state context.
|
|
112
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
113
|
-
| **mustProxify** | `MustProxify`
|
|
108
|
+
| Parameter | Type | Description |
|
|
109
|
+
| --------------- | -------------------------- | --------------------------------------------------------------------- |
|
|
110
|
+
| **context** | `unknown` | The state context. |
|
|
111
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
112
|
+
| **mustProxify** | `MustProxify` _(optional)_ | Predicate determining whether a nested state value must be proxified. |
|
|
114
113
|
|
|
115
114
|
**Returns:**
|
|
116
115
|
`unknown` — the state item value if it was already being watched; otherwise `undefined`.
|
|
@@ -118,46 +117,50 @@ Watches a state item identified by the `(context, index, mustProxify)` triplet s
|
|
|
118
117
|
---
|
|
119
118
|
|
|
120
119
|
### **releaseState(context, index, mustProxify?)**
|
|
120
|
+
|
|
121
121
|
Releases the state item identified by the `(context, index, mustProxify)` triplet.
|
|
122
122
|
Each call to `watchState` should have a corresponding `releaseState` call to ensure the state item is released when it is no longer needed.
|
|
123
123
|
|
|
124
|
-
| Parameter | Type | Description
|
|
125
|
-
| --------------- | -------------------------- |
|
|
126
|
-
| **context** | `unknown` | The state context.
|
|
127
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
128
|
-
| **mustProxify** | `MustProxify`
|
|
124
|
+
| Parameter | Type | Description |
|
|
125
|
+
| --------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
126
|
+
| **context** | `unknown` | The state context. |
|
|
127
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
128
|
+
| **mustProxify** | `MustProxify` _(optional)_ | Predicate determining whether a nested state value must be proxified. It should be the same predicate that was passed to `watchState`. . |
|
|
129
129
|
|
|
130
130
|
**Returns:** `void`
|
|
131
131
|
|
|
132
132
|
---
|
|
133
133
|
|
|
134
134
|
### **getState(context, index)**
|
|
135
|
+
|
|
135
136
|
Retrieves the current state item value identified by the `(context, index)` pair.
|
|
136
137
|
|
|
137
|
-
| Parameter | Type | Description
|
|
138
|
-
| ----------- | --------- |
|
|
139
|
-
| **context** | `unknown` | The state context.
|
|
140
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
138
|
+
| Parameter | Type | Description |
|
|
139
|
+
| ----------- | --------- | ----------------------------------------------------- |
|
|
140
|
+
| **context** | `unknown` | The state context. |
|
|
141
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
141
142
|
|
|
142
143
|
**Returns:** `unknown`
|
|
143
144
|
|
|
144
145
|
---
|
|
145
146
|
|
|
146
147
|
### **setState(context, index, value)**
|
|
148
|
+
|
|
147
149
|
Sets the value of the state item identified by the `(context, index)` pair.
|
|
148
150
|
|
|
149
151
|
Unlike `watchState`, `setState` does **not** track changes. It does not patch setters or proxify values.
|
|
150
152
|
A change event is emitted on the first `setState` call and again whenever the value changes in subsequent calls.
|
|
151
153
|
|
|
152
|
-
| Parameter | Type | Description
|
|
153
|
-
| ----------- | --------- |
|
|
154
|
-
| **context** | `unknown` | The state context.
|
|
155
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
156
|
-
| **value** | `unknown` | The state value.
|
|
154
|
+
| Parameter | Type | Description |
|
|
155
|
+
| ----------- | --------- | ----------------------------------------------------- |
|
|
156
|
+
| **context** | `unknown` | The state context. |
|
|
157
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
158
|
+
| **value** | `unknown` | The state value. |
|
|
157
159
|
|
|
158
160
|
---
|
|
159
161
|
|
|
160
162
|
### **clear()**
|
|
163
|
+
|
|
161
164
|
Releases all registered state items.
|
|
162
165
|
|
|
163
166
|
**Returns:** `void`
|
|
@@ -183,10 +186,13 @@ There are two ways to get an instance:
|
|
|
183
186
|
|
|
184
187
|
```ts
|
|
185
188
|
import { InjectionContainer } from '@rs-x/core';
|
|
186
|
-
import {
|
|
189
|
+
import {
|
|
190
|
+
IIStateManager,
|
|
191
|
+
RsXStateManagerInjectionTokens,
|
|
192
|
+
} from '@rs-x/state-manager';
|
|
187
193
|
|
|
188
194
|
const stateManager: IIStateManager = InjectionContainer.get(
|
|
189
|
-
|
|
195
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
190
196
|
);
|
|
191
197
|
```
|
|
192
198
|
|
|
@@ -194,14 +200,16 @@ const stateManager: IIStateManager = InjectionContainer.get(
|
|
|
194
200
|
|
|
195
201
|
```ts
|
|
196
202
|
import { Inject } from '@rs-x/core';
|
|
197
|
-
import {
|
|
203
|
+
import {
|
|
204
|
+
IIStateManager,
|
|
205
|
+
RsXStateManagerInjectionTokens,
|
|
206
|
+
} from '@rs-x/state-manager';
|
|
198
207
|
|
|
199
208
|
export class MyClass {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
) {}
|
|
209
|
+
constructor(
|
|
210
|
+
@Inject(RsXStateManagerInjectionTokens.IStateManager)
|
|
211
|
+
private readonly _stateManager: IIStateManager,
|
|
212
|
+
) {}
|
|
205
213
|
}
|
|
206
214
|
```
|
|
207
215
|
|
|
@@ -211,60 +219,61 @@ export class MyClass {
|
|
|
211
219
|
|
|
212
220
|
There are two variants:
|
|
213
221
|
|
|
214
|
-
### Non-recursive
|
|
222
|
+
### Non-recursive
|
|
223
|
+
|
|
215
224
|
Monitors only assignment of a **new value** to the index.
|
|
216
225
|
|
|
217
226
|
```ts
|
|
218
227
|
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
219
228
|
import {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
IStateChange,
|
|
230
|
+
IStateManager,
|
|
231
|
+
RsXStateManagerInjectionTokens,
|
|
232
|
+
RsXStateManagerModule,
|
|
224
233
|
} from '@rs-x/state-manager';
|
|
225
234
|
|
|
226
235
|
// Load the state manager module into the injection container
|
|
227
236
|
InjectionContainer.load(RsXStateManagerModule);
|
|
228
237
|
|
|
229
238
|
export const run = (() => {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
240
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const stateContext = {
|
|
244
|
+
x: { y: 10 },
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// This will emit a change event with the initial (current) value.
|
|
248
|
+
console.log('Initial value:');
|
|
249
|
+
const changedSubsription = stateManager.changed.subscribe(
|
|
250
|
+
(change: IStateChange) => {
|
|
251
|
+
printValue(change.newValue);
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// This will emit the new value { y: 10 }
|
|
257
|
+
stateManager.watchState(stateContext, 'x');
|
|
258
|
+
|
|
259
|
+
console.log('Changed value:');
|
|
260
|
+
// This will emit the new value { y: 10 }
|
|
261
|
+
stateContext.x = {
|
|
262
|
+
y: 20,
|
|
236
263
|
};
|
|
237
264
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
// This will emit the new value { y: 10 }
|
|
250
|
-
stateContext.x = {
|
|
251
|
-
y: 20
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
console.log(`Latest value:`);
|
|
255
|
-
printValue(stateManager.getState(stateContext, 'x'));
|
|
256
|
-
|
|
257
|
-
// This will emit no change because the state is not recursive.
|
|
258
|
-
console.log('\nstateContext.x.y = 30 will not emit any change:\n---\n');
|
|
259
|
-
stateContext.x.y = 30;
|
|
260
|
-
|
|
261
|
-
} finally {
|
|
262
|
-
changedSubsription.unsubscribe();
|
|
263
|
-
// Always release the state when it is no longer needed.
|
|
264
|
-
stateManager.releaseState(stateContext, 'x');
|
|
265
|
-
}
|
|
265
|
+
console.log(`Latest value:`);
|
|
266
|
+
printValue(stateManager.getState(stateContext, 'x'));
|
|
267
|
+
|
|
268
|
+
// This will emit no change because the state is not recursive.
|
|
269
|
+
console.log('\nstateContext.x.y = 30 will not emit any change:\n---\n');
|
|
270
|
+
stateContext.x.y = 30;
|
|
271
|
+
} finally {
|
|
272
|
+
changedSubsription.unsubscribe();
|
|
273
|
+
// Always release the state when it is no longer needed.
|
|
274
|
+
stateManager.releaseState(stateContext, 'x');
|
|
275
|
+
}
|
|
266
276
|
})();
|
|
267
|
-
|
|
268
277
|
```
|
|
269
278
|
|
|
270
279
|
**Output:**
|
|
@@ -290,61 +299,63 @@ stateContext.x.y = 30 will not emit any change:
|
|
|
290
299
|
|
|
291
300
|
---
|
|
292
301
|
|
|
293
|
-
### Recursive
|
|
294
|
-
|
|
302
|
+
### Recursive
|
|
303
|
+
|
|
304
|
+
Monitors assignments **and** changes _inside_ the value.
|
|
295
305
|
Example: if the value is an object, internal object changes are also observed. You can make a state item recursive by passing in a **mustProxify** predicate to a **watchState** call. The mustProxify will be called for every nested index. If you return true it will watch the index otherwise not.
|
|
296
306
|
|
|
297
307
|
```ts
|
|
298
308
|
import { InjectionContainer, printValue, truePredicate } from '@rs-x/core';
|
|
299
309
|
import {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
310
|
+
IStateChange,
|
|
311
|
+
IStateManager,
|
|
312
|
+
RsXStateManagerInjectionTokens,
|
|
313
|
+
RsXStateManagerModule,
|
|
304
314
|
} from '@rs-x/state-manager';
|
|
305
315
|
|
|
306
316
|
// Load the state manager module into the injection container
|
|
307
317
|
InjectionContainer.load(RsXStateManagerModule);
|
|
308
318
|
|
|
309
319
|
export const run = (() => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
// This will emit the new value { y: 10 }
|
|
331
|
-
stateContext.x = {
|
|
332
|
-
y: 20
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
console.log('Changed (recursive) value:');
|
|
336
|
-
// This will emit the new value { y: 30 } because x
|
|
337
|
-
// is registered as a recursive state.
|
|
338
|
-
stateContext.x.y = 30;
|
|
320
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
321
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
322
|
+
);
|
|
323
|
+
const stateContext = {
|
|
324
|
+
x: { y: 10 },
|
|
325
|
+
};
|
|
326
|
+
const changedSubscription = stateManager.changed.subscribe(
|
|
327
|
+
(change: IStateChange) => {
|
|
328
|
+
printValue(change.newValue);
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
// We register recursive state by passing
|
|
334
|
+
// a predicate as the third argument.
|
|
335
|
+
// In this case, we want to watch the entire value,
|
|
336
|
+
// so we pass a predicate that always returns true.
|
|
337
|
+
// This will emit an initial value { y: 10 }
|
|
338
|
+
console.log('Initial value:');
|
|
339
|
+
stateManager.watchState(stateContext, 'x', truePredicate);
|
|
339
340
|
|
|
340
|
-
|
|
341
|
-
|
|
341
|
+
console.log('Changed value:');
|
|
342
|
+
// This will emit the new value { y: 10 }
|
|
343
|
+
stateContext.x = {
|
|
344
|
+
y: 20,
|
|
345
|
+
};
|
|
342
346
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
347
|
+
console.log('Changed (recursive) value:');
|
|
348
|
+
// This will emit the new value { y: 30 } because x
|
|
349
|
+
// is registered as a recursive state.
|
|
350
|
+
stateContext.x.y = 30;
|
|
351
|
+
|
|
352
|
+
console.log(`Latest value:`);
|
|
353
|
+
printValue(stateManager.getState(stateContext, 'x'));
|
|
354
|
+
} finally {
|
|
355
|
+
changedSubscription.unsubscribe();
|
|
356
|
+
// Always release the state when it is no longer needed.
|
|
357
|
+
stateManager.releaseState(stateContext, 'x', truePredicate);
|
|
358
|
+
}
|
|
348
359
|
})();
|
|
349
360
|
```
|
|
350
361
|
|
|
@@ -370,82 +381,87 @@ Besides that you can register a watched stated (calling `watchedState`) you can
|
|
|
370
381
|
```ts
|
|
371
382
|
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
372
383
|
import {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
384
|
+
IStateChange,
|
|
385
|
+
IStateManager,
|
|
386
|
+
RsXStateManagerInjectionTokens,
|
|
387
|
+
RsXStateManagerModule,
|
|
377
388
|
} from '@rs-x/state-manager';
|
|
378
389
|
|
|
379
390
|
// Load the state manager module into the injection container
|
|
380
391
|
InjectionContainer.load(RsXStateManagerModule);
|
|
381
392
|
|
|
382
393
|
export const run = (() => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
class StateContext {
|
|
388
|
-
private readonly _aPlusBId = 'a+b';
|
|
389
|
-
private _a = 10;
|
|
390
|
-
private _b = 20;
|
|
394
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
395
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
396
|
+
);
|
|
391
397
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
398
|
+
class StateContext {
|
|
399
|
+
private readonly _aPlusBId = 'a+b';
|
|
400
|
+
private _a = 10;
|
|
401
|
+
private _b = 20;
|
|
395
402
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
403
|
+
constructor() {
|
|
404
|
+
this.setAPlusB();
|
|
405
|
+
}
|
|
399
406
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
407
|
+
public dispose(): void {
|
|
408
|
+
return stateManager.releaseState(this, this._aPlusBId);
|
|
409
|
+
}
|
|
403
410
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
411
|
+
public get aPlusB(): number {
|
|
412
|
+
return stateManager.getState(this, this._aPlusBId);
|
|
413
|
+
}
|
|
407
414
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
415
|
+
public get a(): number {
|
|
416
|
+
return this._a;
|
|
417
|
+
}
|
|
412
418
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
419
|
+
public set a(value: number) {
|
|
420
|
+
this._a = value;
|
|
421
|
+
this.setAPlusB();
|
|
422
|
+
}
|
|
416
423
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
424
|
+
public get b(): number {
|
|
425
|
+
return this._b;
|
|
426
|
+
}
|
|
421
427
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
428
|
+
public set b(value: number) {
|
|
429
|
+
this._b = value;
|
|
430
|
+
this.setAPlusB();
|
|
425
431
|
}
|
|
426
432
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
433
|
+
private setAPlusB(): void {
|
|
434
|
+
stateManager.setState(this, this._aPlusBId, this._a + this._b);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
431
437
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
438
|
+
const stateContext = new StateContext();
|
|
439
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
440
|
+
(change: IStateChange) => {
|
|
441
|
+
printValue(change.newValue);
|
|
442
|
+
},
|
|
443
|
+
);
|
|
435
444
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
445
|
+
try {
|
|
446
|
+
console.log(`Initial value for readonly property 'aPlusB':`);
|
|
447
|
+
console.log(stateContext.aPlusB);
|
|
439
448
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
449
|
+
console.log(
|
|
450
|
+
`set 'stateContext.a' to '100' will emit a change event for readonly property 'aPlusB'`,
|
|
451
|
+
);
|
|
452
|
+
console.log(`Changed value for readonly property 'aPlusB':`);
|
|
453
|
+
stateContext.a = 100;
|
|
443
454
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
455
|
+
console.log(
|
|
456
|
+
`set 'stateContext.b' to '200' will emit a change event for readonly property 'aPlusB'`,
|
|
457
|
+
);
|
|
458
|
+
console.log(`Changed value for readonly property 'aPlusB':`);
|
|
459
|
+
stateContext.b = 200;
|
|
460
|
+
} finally {
|
|
461
|
+
changeSubscription.unsubscribe();
|
|
462
|
+
// Always release the state when it is no longer needed.
|
|
463
|
+
stateContext.dispose();
|
|
464
|
+
}
|
|
449
465
|
})();
|
|
450
466
|
```
|
|
451
467
|
|
|
@@ -476,52 +492,57 @@ When done, release the state:
|
|
|
476
492
|
```ts
|
|
477
493
|
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
478
494
|
import {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
495
|
+
IStateChange,
|
|
496
|
+
IStateManager,
|
|
497
|
+
RsXStateManagerInjectionTokens,
|
|
498
|
+
RsXStateManagerModule,
|
|
483
499
|
} from '@rs-x/state-manager';
|
|
484
500
|
|
|
485
501
|
// Load the state manager module into the injection container
|
|
486
502
|
InjectionContainer.load(RsXStateManagerModule);
|
|
487
503
|
|
|
488
504
|
export const run = (() => {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
stateContext.x = { y: 20 };
|
|
505
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
506
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
507
|
+
);
|
|
508
|
+
const stateContext = {
|
|
509
|
+
x: { y: 10 },
|
|
510
|
+
};
|
|
511
|
+
const changedSubscription = stateManager.changed.subscribe(
|
|
512
|
+
(change: IStateChange) => {
|
|
513
|
+
printValue(change.newValue);
|
|
514
|
+
},
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
// Register is idempotent: you can register the same state multiple times.
|
|
519
|
+
// For every register call, make sure you call unregister when you're done.
|
|
520
|
+
console.log('Initial value:');
|
|
521
|
+
stateManager.watchState(stateContext, 'x');
|
|
522
|
+
stateManager.watchState(stateContext, 'x');
|
|
508
523
|
|
|
509
|
-
|
|
524
|
+
console.log('Changed value:');
|
|
525
|
+
stateContext.x = { y: 20 };
|
|
510
526
|
|
|
511
|
-
|
|
512
|
-
console.log('Changed value:');
|
|
513
|
-
stateContext.x = { y: 30 };
|
|
527
|
+
stateManager.releaseState(stateContext, 'x');
|
|
514
528
|
|
|
515
|
-
|
|
529
|
+
console.log(
|
|
530
|
+
'Changed event is still emitted after unregister because one observer remains.',
|
|
531
|
+
);
|
|
532
|
+
console.log('Changed value:');
|
|
533
|
+
stateContext.x = { y: 30 };
|
|
516
534
|
|
|
517
|
-
|
|
518
|
-
console.log('Changed value:');
|
|
519
|
-
console.log('---');
|
|
520
|
-
stateContext.x = { y: 30 };
|
|
535
|
+
stateManager.releaseState(stateContext, 'x');
|
|
521
536
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
537
|
+
console.log(
|
|
538
|
+
'Changed event is no longer emitted after the last observer unregisters.',
|
|
539
|
+
);
|
|
540
|
+
console.log('Changed value:');
|
|
541
|
+
console.log('---');
|
|
542
|
+
stateContext.x = { y: 30 };
|
|
543
|
+
} finally {
|
|
544
|
+
changedSubscription.unsubscribe();
|
|
545
|
+
}
|
|
525
546
|
})();
|
|
526
547
|
```
|
|
527
548
|
|
|
@@ -560,7 +581,7 @@ You can override this factory list by providing your own custom provider service
|
|
|
560
581
|
| Observable | Not indexable | Subscribe | [example](#observable) |
|
|
561
582
|
| Custom type | user defined | user defined | [example](#customtype) |
|
|
562
583
|
|
|
563
|
-
State item is identified by a **context**,
|
|
584
|
+
State item is identified by a **context**, **index** and **mustProxify** predicate for a recursive state item
|
|
564
585
|
The manager checks each observer factory to determine support based on the **context** and **index**.
|
|
565
586
|
|
|
566
587
|
Behavior:
|
|
@@ -571,86 +592,93 @@ Behavior:
|
|
|
571
592
|
The following example illustrates the different state types:
|
|
572
593
|
|
|
573
594
|
### Object property/field
|
|
595
|
+
|
|
574
596
|
**Example**
|
|
597
|
+
|
|
575
598
|
```ts
|
|
576
599
|
import { InjectionContainer, printValue, truePredicate } from '@rs-x/core';
|
|
577
600
|
import {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
601
|
+
IStateChange,
|
|
602
|
+
IStateManager,
|
|
603
|
+
RsXStateManagerInjectionTokens,
|
|
604
|
+
RsXStateManagerModule,
|
|
582
605
|
} from '@rs-x/state-manager';
|
|
583
606
|
|
|
584
607
|
// Load the state manager module into the injection container
|
|
585
608
|
InjectionContainer.load(RsXStateManagerModule);
|
|
586
609
|
|
|
587
610
|
interface INestStateConext {
|
|
588
|
-
|
|
589
|
-
|
|
611
|
+
a: number;
|
|
612
|
+
nested?: INestStateConext;
|
|
590
613
|
}
|
|
591
614
|
|
|
592
615
|
class StateContext {
|
|
593
|
-
|
|
594
|
-
|
|
616
|
+
private _b: INestStateConext = {
|
|
617
|
+
a: 10,
|
|
618
|
+
nested: {
|
|
619
|
+
a: 20,
|
|
620
|
+
nested: {
|
|
621
|
+
a: 30,
|
|
595
622
|
nested: {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
public set b(value: INestStateConext) {
|
|
611
|
-
this._b = value;
|
|
612
|
-
}
|
|
623
|
+
a: 40,
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
public get b(): INestStateConext {
|
|
630
|
+
return this._b;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
public set b(value: INestStateConext) {
|
|
634
|
+
this._b = value;
|
|
635
|
+
}
|
|
613
636
|
}
|
|
614
637
|
|
|
615
638
|
export const run = (() => {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
639
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
640
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
641
|
+
);
|
|
619
642
|
|
|
620
|
-
|
|
643
|
+
const stateContext = new StateContext();
|
|
621
644
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
// Observe property `b` recursively.
|
|
628
|
-
// Otherwise, only assigning a new value to stateContext.b would emit a change event.
|
|
629
|
-
// This will emit a change event with the initial (current) value.
|
|
630
|
-
console.log('Initial value:');
|
|
631
|
-
stateManager.watchState(stateContext, 'b', truePredicate);
|
|
645
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
646
|
+
(change: IStateChange) => {
|
|
647
|
+
printValue(change.newValue);
|
|
648
|
+
},
|
|
649
|
+
);
|
|
632
650
|
|
|
633
|
-
|
|
634
|
-
|
|
651
|
+
try {
|
|
652
|
+
// Observe property `b` recursively.
|
|
653
|
+
// Otherwise, only assigning a new value to stateContext.b would emit a change event.
|
|
654
|
+
// This will emit a change event with the initial (current) value.
|
|
655
|
+
console.log('Initial value:');
|
|
656
|
+
stateManager.watchState(stateContext, 'b', truePredicate);
|
|
635
657
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
641
|
-
};
|
|
658
|
+
console.log(
|
|
659
|
+
'\nReplacing stateContext.b.nested.nested will emit a change event',
|
|
660
|
+
);
|
|
661
|
+
console.log('Changed value:');
|
|
642
662
|
|
|
643
|
-
|
|
644
|
-
|
|
663
|
+
stateContext.b.nested.nested = {
|
|
664
|
+
a: -30,
|
|
665
|
+
nested: {
|
|
666
|
+
a: -40,
|
|
667
|
+
},
|
|
668
|
+
};
|
|
645
669
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
670
|
+
console.log(`Latest value:`);
|
|
671
|
+
printValue(stateManager.getState(stateContext, 'b'));
|
|
672
|
+
} finally {
|
|
673
|
+
changeSubscription.unsubscribe();
|
|
674
|
+
// Always release the state when it is no longer needed.
|
|
675
|
+
stateManager.releaseState(stateContext, 'b', truePredicate);
|
|
676
|
+
}
|
|
651
677
|
})();
|
|
652
678
|
```
|
|
679
|
+
|
|
653
680
|
**Output:**
|
|
681
|
+
|
|
654
682
|
```console
|
|
655
683
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-property.ts
|
|
656
684
|
Initial value:
|
|
@@ -697,86 +725,97 @@ Latest value:
|
|
|
697
725
|
```
|
|
698
726
|
|
|
699
727
|
### Date
|
|
728
|
+
|
|
700
729
|
**Example**
|
|
730
|
+
|
|
701
731
|
```ts
|
|
702
732
|
import { InjectionContainer, truePredicate, utCDate } from '@rs-x/core';
|
|
703
733
|
import {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
734
|
+
IProxyRegistry,
|
|
735
|
+
IStateChange,
|
|
736
|
+
IStateManager,
|
|
737
|
+
RsXStateManagerInjectionTokens,
|
|
738
|
+
RsXStateManagerModule,
|
|
709
739
|
} from '@rs-x/state-manager';
|
|
710
740
|
|
|
711
741
|
// Load the state manager module into the injection container
|
|
712
742
|
InjectionContainer.load(RsXStateManagerModule);
|
|
713
743
|
|
|
714
744
|
function watchDate(stateManager: IStateManager) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
745
|
+
console.log('\n******************************************');
|
|
746
|
+
console.log('* Watching date');
|
|
747
|
+
console.log('******************************************\n');
|
|
748
|
+
|
|
749
|
+
const stateContext = {
|
|
750
|
+
date: utCDate(2021, 2, 5),
|
|
751
|
+
};
|
|
752
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
753
|
+
(change: IStateChange) => {
|
|
754
|
+
console.log(`${change.key}: ${(change.newValue as Date).toUTCString()}`);
|
|
755
|
+
},
|
|
756
|
+
);
|
|
757
|
+
try {
|
|
758
|
+
console.log('Initial value:');
|
|
759
|
+
stateManager.watchState(stateContext, 'date', truePredicate);
|
|
728
760
|
|
|
729
|
-
|
|
730
|
-
|
|
761
|
+
console.log('Changed value:');
|
|
762
|
+
stateContext.date.setFullYear(2023);
|
|
731
763
|
|
|
732
|
-
|
|
733
|
-
|
|
764
|
+
console.log('Set value:');
|
|
765
|
+
stateContext.date = new Date(2024, 5, 6);
|
|
734
766
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
767
|
+
console.log('Latest value:');
|
|
768
|
+
console.log(
|
|
769
|
+
stateManager.getState<Date>(stateContext, 'date').toUTCString(),
|
|
770
|
+
);
|
|
771
|
+
} finally {
|
|
772
|
+
changeSubscription.unsubscribe();
|
|
773
|
+
// Always release the state when it is no longer needed.
|
|
774
|
+
stateManager.releaseState(stateContext, 'date', truePredicate);
|
|
775
|
+
}
|
|
742
776
|
}
|
|
743
777
|
|
|
744
778
|
function watchDateProperty(stateManager: IStateManager) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const dateProxy = proxyRegister.getProxy<Date>(date);
|
|
759
|
-
console.log('Changed value:');
|
|
760
|
-
dateProxy.setFullYear(2023);
|
|
761
|
-
|
|
762
|
-
console.log('Latest value:');
|
|
763
|
-
console.log(stateManager.getState(date, 'year'));
|
|
779
|
+
console.log('\n******************************************');
|
|
780
|
+
console.log('* Watching year');
|
|
781
|
+
console.log('******************************************\n');
|
|
782
|
+
const date = utCDate(2021, 2, 5);
|
|
783
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
784
|
+
(change: IStateChange) => {
|
|
785
|
+
console.log(change.newValue);
|
|
786
|
+
},
|
|
787
|
+
);
|
|
788
|
+
try {
|
|
789
|
+
// This will emit a change event with the initial (current) value.
|
|
790
|
+
console.log('Initial value:');
|
|
791
|
+
stateManager.watchState(date, 'year');
|
|
764
792
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
793
|
+
const proxyRegister: IProxyRegistry = InjectionContainer.get(
|
|
794
|
+
RsXStateManagerInjectionTokens.IProxyRegistry,
|
|
795
|
+
);
|
|
796
|
+
const dateProxy = proxyRegister.getProxy<Date>(date);
|
|
797
|
+
console.log('Changed value:');
|
|
798
|
+
dateProxy.setFullYear(2023);
|
|
799
|
+
|
|
800
|
+
console.log('Latest value:');
|
|
801
|
+
console.log(stateManager.getState(date, 'year'));
|
|
802
|
+
} finally {
|
|
803
|
+
changeSubscription.unsubscribe();
|
|
804
|
+
stateManager.releaseState(date, 'year');
|
|
805
|
+
}
|
|
769
806
|
}
|
|
770
807
|
|
|
771
808
|
export const run = (() => {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
809
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
810
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
811
|
+
);
|
|
812
|
+
watchDate(stateManager);
|
|
813
|
+
watchDateProperty(stateManager);
|
|
777
814
|
})();
|
|
778
815
|
```
|
|
816
|
+
|
|
779
817
|
**Output:**
|
|
818
|
+
|
|
780
819
|
```console
|
|
781
820
|
Running demo: demo/src/rs-x-state-manager/register-date.ts
|
|
782
821
|
|
|
@@ -806,54 +845,59 @@ Latest value:
|
|
|
806
845
|
```
|
|
807
846
|
|
|
808
847
|
### Array
|
|
848
|
+
|
|
809
849
|
**Example**
|
|
850
|
+
|
|
810
851
|
```ts
|
|
811
852
|
import { InjectionContainer, printValue, truePredicate } from '@rs-x/core';
|
|
812
853
|
import {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
854
|
+
IStateChange,
|
|
855
|
+
IStateManager,
|
|
856
|
+
RsXStateManagerInjectionTokens,
|
|
857
|
+
RsXStateManagerModule,
|
|
817
858
|
} from '@rs-x/state-manager';
|
|
818
859
|
|
|
819
860
|
// Load the state manager module into the injection container
|
|
820
861
|
InjectionContainer.load(RsXStateManagerModule);
|
|
821
862
|
|
|
822
863
|
export const run = (() => {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const changeSubscription = stateManager.changed.subscribe((change: IStateChange) => {
|
|
835
|
-
printValue(change.newValue);
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
try {
|
|
839
|
-
// This will emit a change event with the initial (current) value.
|
|
840
|
-
console.log('Initial value:');
|
|
841
|
-
stateManager.watchState(stateContext, 'array', truePredicate);
|
|
842
|
-
|
|
843
|
-
console.log('Changed value:');
|
|
844
|
-
stateContext.array[1].push(5);
|
|
864
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
865
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
const stateContext = {
|
|
869
|
+
array: [
|
|
870
|
+
[1, 2],
|
|
871
|
+
[3, 4],
|
|
872
|
+
],
|
|
873
|
+
};
|
|
845
874
|
|
|
846
|
-
|
|
847
|
-
|
|
875
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
876
|
+
(change: IStateChange) => {
|
|
877
|
+
printValue(change.newValue);
|
|
878
|
+
},
|
|
879
|
+
);
|
|
848
880
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
881
|
+
try {
|
|
882
|
+
// This will emit a change event with the initial (current) value.
|
|
883
|
+
console.log('Initial value:');
|
|
884
|
+
stateManager.watchState(stateContext, 'array', truePredicate);
|
|
885
|
+
|
|
886
|
+
console.log('Changed value:');
|
|
887
|
+
stateContext.array[1].push(5);
|
|
888
|
+
|
|
889
|
+
console.log('Latest value:');
|
|
890
|
+
printValue(stateManager.getState(stateContext, 'array'));
|
|
891
|
+
} finally {
|
|
892
|
+
changeSubscription.unsubscribe();
|
|
893
|
+
// Always release the state when it is no longer needed.
|
|
894
|
+
stateManager.releaseState(stateContext, 'array', truePredicate);
|
|
895
|
+
}
|
|
854
896
|
})();
|
|
855
897
|
```
|
|
898
|
+
|
|
856
899
|
**Output:**
|
|
900
|
+
|
|
857
901
|
```console
|
|
858
902
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-array.ts
|
|
859
903
|
Initial value:
|
|
@@ -894,54 +938,59 @@ Latest value:
|
|
|
894
938
|
```
|
|
895
939
|
|
|
896
940
|
### Map
|
|
941
|
+
|
|
897
942
|
**Example**
|
|
943
|
+
|
|
898
944
|
```ts
|
|
899
945
|
import { InjectionContainer, printValue, truePredicate } from '@rs-x/core';
|
|
900
946
|
import {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
947
|
+
IStateChange,
|
|
948
|
+
IStateManager,
|
|
949
|
+
RsXStateManagerInjectionTokens,
|
|
950
|
+
RsXStateManagerModule,
|
|
905
951
|
} from '@rs-x/state-manager';
|
|
906
952
|
|
|
907
953
|
// Load the state manager module into the injection container
|
|
908
954
|
InjectionContainer.load(RsXStateManagerModule);
|
|
909
955
|
|
|
910
956
|
const stateManager: IStateManager = InjectionContainer.get(
|
|
911
|
-
|
|
957
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
912
958
|
);
|
|
913
959
|
|
|
914
960
|
export const run = (() => {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
961
|
+
const stateContext = {
|
|
962
|
+
map: new Map([
|
|
963
|
+
['a', [1, 2]],
|
|
964
|
+
['b', [3, 4]],
|
|
965
|
+
]),
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
969
|
+
(change: IStateChange) => {
|
|
970
|
+
printValue(change.newValue);
|
|
971
|
+
},
|
|
972
|
+
);
|
|
973
|
+
|
|
974
|
+
try {
|
|
975
|
+
// This will emit a change event with the initial (current) value.
|
|
976
|
+
console.log('Initial value:');
|
|
977
|
+
stateManager.watchState(stateContext, 'map', truePredicate);
|
|
978
|
+
|
|
979
|
+
console.log('Changed value:');
|
|
980
|
+
stateContext.map.get('b').push(5);
|
|
981
|
+
|
|
982
|
+
console.log('Latest value:');
|
|
983
|
+
printValue(stateManager.getState(stateContext, 'map'));
|
|
984
|
+
} finally {
|
|
985
|
+
changeSubscription.unsubscribe();
|
|
986
|
+
// Always release the state when it is no longer needed.
|
|
987
|
+
stateManager.releaseState(stateContext, 'array', truePredicate);
|
|
988
|
+
}
|
|
942
989
|
})();
|
|
943
990
|
```
|
|
991
|
+
|
|
944
992
|
**Output:**
|
|
993
|
+
|
|
945
994
|
```console
|
|
946
995
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-map.ts
|
|
947
996
|
Initial value:
|
|
@@ -1000,53 +1049,60 @@ Latest value:
|
|
|
1000
1049
|
```
|
|
1001
1050
|
|
|
1002
1051
|
### Set
|
|
1052
|
+
|
|
1003
1053
|
**Example**
|
|
1054
|
+
|
|
1004
1055
|
```ts
|
|
1005
1056
|
import { InjectionContainer, printValue, truePredicate } from '@rs-x/core';
|
|
1006
1057
|
import {
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1058
|
+
IProxyRegistry,
|
|
1059
|
+
IStateChange,
|
|
1060
|
+
IStateManager,
|
|
1061
|
+
RsXStateManagerInjectionTokens,
|
|
1062
|
+
RsXStateManagerModule,
|
|
1012
1063
|
} from '@rs-x/state-manager';
|
|
1013
1064
|
|
|
1014
1065
|
// Load the state manager module into the injection container
|
|
1015
1066
|
InjectionContainer.load(RsXStateManagerModule);
|
|
1016
1067
|
|
|
1017
1068
|
export const run = (() => {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const proxyRegister: IProxyRegistry = InjectionContainer.get(RsXStateManagerInjectionTokens.IProxyRegistry);
|
|
1037
|
-
proxyRegister.getProxy<number[]>(item2).push(5);
|
|
1038
|
-
|
|
1039
|
-
console.log('Latest value:');
|
|
1040
|
-
printValue(stateManager.getState(stateContext, 'set'));
|
|
1069
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
1070
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
1071
|
+
);
|
|
1072
|
+
const item1 = [1, 2];
|
|
1073
|
+
const item2 = [3, 4];
|
|
1074
|
+
const stateContext = {
|
|
1075
|
+
set: new Set([item1, item2]),
|
|
1076
|
+
};
|
|
1077
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
1078
|
+
(change: IStateChange) => {
|
|
1079
|
+
printValue(change.newValue);
|
|
1080
|
+
},
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
try {
|
|
1084
|
+
// This will emit a change event with the initial (current) value.
|
|
1085
|
+
console.log('Initial value:');
|
|
1086
|
+
stateManager.watchState(stateContext, 'set', truePredicate);
|
|
1041
1087
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1088
|
+
console.log('Changed value:');
|
|
1089
|
+
const proxyRegister: IProxyRegistry = InjectionContainer.get(
|
|
1090
|
+
RsXStateManagerInjectionTokens.IProxyRegistry,
|
|
1091
|
+
);
|
|
1092
|
+
proxyRegister.getProxy<number[]>(item2).push(5);
|
|
1093
|
+
|
|
1094
|
+
console.log('Latest value:');
|
|
1095
|
+
printValue(stateManager.getState(stateContext, 'set'));
|
|
1096
|
+
} finally {
|
|
1097
|
+
changeSubscription.unsubscribe();
|
|
1098
|
+
// Always release the state when it is no longer needed.
|
|
1099
|
+
stateManager.releaseState(stateContext, 'set', truePredicate);
|
|
1100
|
+
}
|
|
1047
1101
|
})();
|
|
1048
1102
|
```
|
|
1103
|
+
|
|
1049
1104
|
**Output:**
|
|
1105
|
+
|
|
1050
1106
|
```console
|
|
1051
1107
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-set.ts
|
|
1052
1108
|
Initial value:
|
|
@@ -1087,54 +1143,64 @@ Latest value:
|
|
|
1087
1143
|
```
|
|
1088
1144
|
|
|
1089
1145
|
### Promise
|
|
1146
|
+
|
|
1090
1147
|
**Example**
|
|
1148
|
+
|
|
1091
1149
|
```ts
|
|
1092
1150
|
import { InjectionContainer, WaitForEvent } from '@rs-x/core';
|
|
1093
1151
|
import {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1152
|
+
IStateChange,
|
|
1153
|
+
IStateManager,
|
|
1154
|
+
RsXStateManagerInjectionTokens,
|
|
1155
|
+
RsXStateManagerModule,
|
|
1098
1156
|
} from '@rs-x/state-manager';
|
|
1099
1157
|
|
|
1100
1158
|
// Load the state manager module into the injection container
|
|
1101
1159
|
InjectionContainer.load(RsXStateManagerModule);
|
|
1102
1160
|
|
|
1103
1161
|
export const run = (async () => {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1162
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
1163
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
1164
|
+
);
|
|
1165
|
+
|
|
1166
|
+
const stateContext = {
|
|
1167
|
+
promise: Promise.resolve(10),
|
|
1168
|
+
};
|
|
1169
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
1170
|
+
(change: IStateChange) => {
|
|
1171
|
+
console.log(change.newValue);
|
|
1172
|
+
},
|
|
1173
|
+
);
|
|
1174
|
+
|
|
1175
|
+
try {
|
|
1176
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1177
|
+
// This will emit a change event with the initial (current) value.
|
|
1178
|
+
console.log('Initial value:');
|
|
1179
|
+
stateManager.watchState(stateContext, 'promise');
|
|
1113
1180
|
});
|
|
1114
1181
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
console.log('Changed value:');
|
|
1124
|
-
let resolveHandler: (value: number) => void;
|
|
1125
|
-
stateContext.promise = new Promise((resolve) => { resolveHandler = resolve; });
|
|
1126
|
-
resolveHandler(30);
|
|
1127
|
-
});
|
|
1182
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1183
|
+
console.log('Changed value:');
|
|
1184
|
+
let resolveHandler: (value: number) => void;
|
|
1185
|
+
stateContext.promise = new Promise((resolve) => {
|
|
1186
|
+
resolveHandler = resolve;
|
|
1187
|
+
});
|
|
1188
|
+
resolveHandler(30);
|
|
1189
|
+
});
|
|
1128
1190
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1191
|
+
console.log(
|
|
1192
|
+
`Latest value: ${stateManager.getState(stateContext, 'promise')}`,
|
|
1193
|
+
);
|
|
1194
|
+
} finally {
|
|
1195
|
+
changeSubscription.unsubscribe();
|
|
1196
|
+
// Always release the state when it is no longer needed.
|
|
1197
|
+
stateManager.releaseState(stateContext, 'promise');
|
|
1198
|
+
}
|
|
1135
1199
|
})();
|
|
1136
1200
|
```
|
|
1201
|
+
|
|
1137
1202
|
**Output:**
|
|
1203
|
+
|
|
1138
1204
|
```console
|
|
1139
1205
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-promise.ts
|
|
1140
1206
|
Initial value:
|
|
@@ -1145,14 +1211,16 @@ Latest value: 30
|
|
|
1145
1211
|
```
|
|
1146
1212
|
|
|
1147
1213
|
### Observable
|
|
1214
|
+
|
|
1148
1215
|
**Example**
|
|
1216
|
+
|
|
1149
1217
|
```ts
|
|
1150
1218
|
import { InjectionContainer, WaitForEvent } from '@rs-x/core';
|
|
1151
1219
|
import {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1220
|
+
IStateChange,
|
|
1221
|
+
IStateManager,
|
|
1222
|
+
RsXStateManagerInjectionTokens,
|
|
1223
|
+
RsXStateManagerModule,
|
|
1156
1224
|
} from '@rs-x/state-manager';
|
|
1157
1225
|
import { of, Subject } from 'rxjs';
|
|
1158
1226
|
|
|
@@ -1160,44 +1228,49 @@ import { of, Subject } from 'rxjs';
|
|
|
1160
1228
|
InjectionContainer.load(RsXStateManagerModule);
|
|
1161
1229
|
|
|
1162
1230
|
export const run = (async () => {
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1231
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
1232
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
1233
|
+
);
|
|
1234
|
+
|
|
1235
|
+
const stateContext = {
|
|
1236
|
+
observable: of(10),
|
|
1237
|
+
};
|
|
1238
|
+
|
|
1239
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
1240
|
+
(change: IStateChange) => {
|
|
1241
|
+
console.log(change.newValue);
|
|
1242
|
+
},
|
|
1243
|
+
);
|
|
1244
|
+
|
|
1245
|
+
try {
|
|
1246
|
+
// We need to wait here until the event is emitted,
|
|
1247
|
+
// otherwise the demo will exit before the change event occurs.
|
|
1248
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1249
|
+
// This will emit a change event with the initial (current) value.
|
|
1250
|
+
console.log('Initial value:');
|
|
1251
|
+
stateManager.watchState(stateContext, 'observable');
|
|
1173
1252
|
});
|
|
1174
1253
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
stateManager.watchState(stateContext, 'observable');
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1185
|
-
console.log('Changed value:');
|
|
1186
|
-
const subject = new Subject<number>();
|
|
1187
|
-
stateContext.observable = subject
|
|
1188
|
-
subject.next(30);
|
|
1189
|
-
});
|
|
1190
|
-
|
|
1191
|
-
console.log(`Latest value: ${stateManager.getState(stateContext, 'observable')}`);
|
|
1254
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1255
|
+
console.log('Changed value:');
|
|
1256
|
+
const subject = new Subject<number>();
|
|
1257
|
+
stateContext.observable = subject;
|
|
1258
|
+
subject.next(30);
|
|
1259
|
+
});
|
|
1192
1260
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1261
|
+
console.log(
|
|
1262
|
+
`Latest value: ${stateManager.getState(stateContext, 'observable')}`,
|
|
1263
|
+
);
|
|
1264
|
+
} finally {
|
|
1265
|
+
changeSubscription.unsubscribe();
|
|
1266
|
+
// Always release the state when it is no longer needed.
|
|
1267
|
+
stateManager.releaseState(stateContext, 'observable');
|
|
1268
|
+
}
|
|
1198
1269
|
})();
|
|
1199
1270
|
```
|
|
1271
|
+
|
|
1200
1272
|
**Output:**
|
|
1273
|
+
|
|
1201
1274
|
```console
|
|
1202
1275
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-observable.ts
|
|
1203
1276
|
Initial value:
|
|
@@ -1208,14 +1281,16 @@ Latest value: 30
|
|
|
1208
1281
|
```
|
|
1209
1282
|
|
|
1210
1283
|
### Custom type
|
|
1211
|
-
|
|
1212
|
-
|
|
1284
|
+
|
|
1285
|
+
1. Create an accessor to retrieve index values on your type.
|
|
1286
|
+
2. Create a factory to create an observer for your data type.
|
|
1213
1287
|
3. Create a factory to create an observer for an index on your data instance.
|
|
1214
1288
|
|
|
1215
1289
|
The following example demonstrates adding support for a custom `TextDocument` class:
|
|
1216
1290
|
|
|
1217
1291
|
**Example**
|
|
1218
|
-
|
|
1292
|
+
|
|
1293
|
+
````ts
|
|
1219
1294
|
import {
|
|
1220
1295
|
ContainerModule,
|
|
1221
1296
|
defaultIndexValueAccessorList,
|
|
@@ -1664,7 +1739,7 @@ function testMonitoreSpecificLineInDocument(stateManager: IStateManager, stateCo
|
|
|
1664
1739
|
});
|
|
1665
1740
|
|
|
1666
1741
|
try {
|
|
1667
|
-
// Here we only watch line 3 on page 1.
|
|
1742
|
+
// Here we only watch line 3 on page 1.
|
|
1668
1743
|
// Notice that the line does not have to exist yet.
|
|
1669
1744
|
// The initial book does not have a line 3 on page 1.
|
|
1670
1745
|
//
|
|
@@ -1684,7 +1759,7 @@ function testMonitoreSpecificLineInDocument(stateManager: IStateManager, stateCo
|
|
|
1684
1759
|
bookProxy.setLine({ pageIndex: 0, lineIndex: 0 }, 'a troll was born');
|
|
1685
1760
|
|
|
1686
1761
|
} finally {
|
|
1687
|
-
// Stop monitoring line 3 on page 1.
|
|
1762
|
+
// Stop monitoring line 3 on page 1.
|
|
1688
1763
|
stateManager.releaseState(stateContext.myBook, line3OnPage1Index);
|
|
1689
1764
|
lineSubscription.unsubscribe();
|
|
1690
1765
|
}
|
|
@@ -1711,8 +1786,10 @@ export const run = (() => {
|
|
|
1711
1786
|
testMonitoreSpecificLineInDocument(stateManager, stateContext);
|
|
1712
1787
|
})();
|
|
1713
1788
|
```nclude_relative ../demo/src/rs-x-state-manager/register-set.ts %}
|
|
1714
|
-
|
|
1789
|
+
````
|
|
1790
|
+
|
|
1715
1791
|
**Output:**
|
|
1792
|
+
|
|
1716
1793
|
```console
|
|
1717
1794
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/state-manager-customize.ts
|
|
1718
1795
|
|
|
@@ -1762,4 +1839,3 @@ Page 1:
|
|
|
1762
1839
|
Changing line 1 on page 1 does not emit change:
|
|
1763
1840
|
---
|
|
1764
1841
|
```
|
|
1765
|
-
|