native-document 1.0.10 → 1.0.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.
@@ -0,0 +1,265 @@
1
+ # Observables
2
+
3
+ Observables are the reactive core of NativeDocument. They allow you to create values that automatically update in the user interface when they change.
4
+
5
+ ## Creating Simple Observables
6
+
7
+ ```javascript
8
+ // Create an observable with an initial value
9
+ const count = Observable(0);
10
+ const message = Observable("Hello World");
11
+ const isVisible = Observable(true);
12
+ ```
13
+
14
+ ## Reading and Modifying Values
15
+
16
+ ```javascript
17
+ const name = Observable("John");
18
+
19
+ // Read the current value
20
+ console.log(name.val()); // "John"
21
+
22
+ // Using the proxy syntax (shorthand)
23
+ console.log(name.$value); // "John"
24
+
25
+ // Update the value
26
+ name.set("Jane");
27
+ console.log(name.val()); // "Jane"
28
+
29
+ // Update using proxy syntax
30
+ name.$value = "Bob";
31
+ console.log(name.val()); // "Bob"
32
+
33
+ // Update with a function
34
+ name.set(currentName => currentName.toUpperCase());
35
+ console.log(name.val()); // "BOB"
36
+ ```
37
+
38
+ ## Listening to Changes
39
+
40
+ ```javascript
41
+ const counter = Observable(0);
42
+
43
+ // Subscribe to changes
44
+ counter.subscribe(newValue => {
45
+ console.log("Counter is now:", newValue);
46
+ });
47
+
48
+ // Function will be called on every change
49
+ counter.set(1); // Logs: "Counter is now: 1"
50
+ counter.set(2); // Logs: "Counter is now: 2"
51
+ ```
52
+
53
+ ## Observable Objects vs Simple Objects
54
+
55
+ **Important distinction:**
56
+
57
+ ```javascript
58
+ // Observable.object() creates a PROXY with reactive properties
59
+ const userProxy = Observable.object({
60
+ name: "Alice",
61
+ age: 25
62
+ });
63
+
64
+ // Each property is an individual observable
65
+ console.log(userProxy.name.val()); // "Alice"
66
+ userProxy.name.set("Bob");
67
+
68
+ // Get all values as plain object
69
+ console.log(userProxy.$val()); // { name: "Bob", age: 25 }
70
+ console.log(Observable.value(userProxy)); // { name: "Bob", age: 25 }
71
+
72
+ // Observable(object) creates a SINGLE observable containing the whole object
73
+ const userSingle = Observable({
74
+ name: "Alice",
75
+ age: 25
76
+ });
77
+
78
+ // The entire object is the observable value
79
+ console.log(userSingle.val()); // { name: "Alice", age: 25 }
80
+ userSingle.set({ name: "Bob", age: 30 }); // Replace entire object
81
+ ```
82
+
83
+ **Observable.object is an alias:**
84
+ ```javascript
85
+ // These are identical
86
+ Observable.object(data) === Observable.json(data) === Observable.init(data)
87
+ ```
88
+
89
+ ## Working with Observable Proxies
90
+
91
+ ```javascript
92
+ const user = Observable.object({
93
+ name: "Alice",
94
+ age: 25,
95
+ email: "alice@example.com"
96
+ });
97
+
98
+ // Access individual properties (each is an observable)
99
+ console.log(user.name.val()); // "Alice"
100
+ console.log(user.name.$value); // "Alice" (proxy syntax)
101
+
102
+ // Update individual properties
103
+ user.name.set("Bob");
104
+ user.age.$value = 30; // Using proxy syntax
105
+
106
+ // Get the complete object value
107
+ console.log(user.$val()); // { name: "Bob", age: 30, email: "alice@example.com" }
108
+ console.log(Observable.value(user)); // Same as above
109
+
110
+ // Listen to individual property changes
111
+ user.name.subscribe(newName => {
112
+ console.log("New name:", newName);
113
+ });
114
+
115
+ // Update multiple properties
116
+ Observable.update(user, {
117
+ name: "Charlie",
118
+ age: 35
119
+ });
120
+ ```
121
+
122
+ ```javascript
123
+ const todos = Observable.array([
124
+ "Buy groceries",
125
+ "Call doctor"
126
+ ]);
127
+
128
+ // Add elements
129
+ todos.push("Clean house");
130
+
131
+ // Remove last element
132
+ todos.pop();
133
+
134
+ // Use array methods
135
+ const completed = todos.filter(todo => todo.includes("✓"));
136
+ ```
137
+
138
+ ## Computed Observables
139
+
140
+ Computed observables automatically recalculate when their dependencies change.
141
+
142
+ ```javascript
143
+ const firstName = Observable("John");
144
+ const lastName = Observable("Doe");
145
+
146
+ // Updates automatically
147
+ const fullName = Observable.computed(() => {
148
+ return `${firstName.val()} ${lastName.val()}`;
149
+ }, [firstName, lastName]);
150
+
151
+ console.log(fullName.val()); // "John Doe"
152
+
153
+ firstName.set("Jane");
154
+ console.log(fullName.val()); // "Jane Doe"
155
+ ```
156
+
157
+ ## Practical Example: Simple Counter
158
+
159
+ ```javascript
160
+ const count = Observable(0);
161
+
162
+ const increment = () => count.set(count.val() + 1);
163
+ const decrement = () => (count.$value--);
164
+
165
+ // Reactive interface
166
+ const app = Div({ class: "counter" }, [
167
+ Button("-").nd.on.click(decrement),
168
+ Span({ class: "count" }, count), // Automatic display
169
+ Button("+").nd.on.click(increment)
170
+ ]);
171
+ ```
172
+
173
+ ## String Templates with Observables
174
+
175
+ ### The .use() Method
176
+
177
+ ```javascript
178
+ const name = Observable("Alice");
179
+ const age = Observable(25);
180
+
181
+ const template = "Hello ${name}, you are ${age} years old";
182
+ const message = template.use({ name, age });
183
+
184
+ console.log(message.val()); // "Hello Alice, you are 25 years old"
185
+
186
+ name.set("Bob");
187
+ console.log(message.val()); // "Hello Bob, you are 25 years old"
188
+ ```
189
+
190
+ ### Automatic Template Resolution
191
+
192
+ ```javascript
193
+ const greeting = Observable("Hello");
194
+ const user = Observable("Marie");
195
+
196
+ // Observables are automatically integrated
197
+ const element = Div(null, `${greeting} ${user}!`);
198
+ // Updates when greeting or user changes
199
+ ```
200
+
201
+ ## Observable Checkers
202
+
203
+ Create derived observables with conditions:
204
+
205
+ ```javascript
206
+ const age = Observable(17);
207
+ const isAdult = age.check(value => value >= 18);
208
+
209
+ console.log(isAdult.val()); // false
210
+
211
+ age.set(20);
212
+ console.log(isAdult.val()); // true
213
+ ```
214
+
215
+ ## Memory Management
216
+
217
+ ```javascript
218
+ const data = Observable("test");
219
+
220
+ // Create a subscription
221
+ const unsubscribe = data.subscribe(value => console.log(value));
222
+
223
+ // Clean up manually if needed
224
+ unsubscribe();
225
+
226
+ // Complete observable cleanup
227
+ data.cleanup(); // Removes all listeners and prevents new subscriptions
228
+
229
+ // Manual trigger (useful for forcing updates)
230
+ data.trigger(); // Notifies all subscribers without changing the value
231
+
232
+ // Get original value (useful for reset functionality)
233
+ console.log(data.originalValue()); // Returns the initial value
234
+
235
+ // Extract values from any observable structure
236
+ const complexData = Observable.object({
237
+ user: "John",
238
+ items: [1, 2, 3]
239
+ });
240
+ console.log(Observable.value(complexData)); // Plain object with extracted values
241
+ ```
242
+
243
+ ## Best Practices
244
+
245
+ 1. **Use descriptive names** for your observables
246
+ 2. **Understand the difference**: `Observable(object)` vs `Observable.object(object)`
247
+ 3. **Use proxies for convenience**: `obs.$value` instead of `obs.val()`
248
+ 4. **Group related data** with `Observable.object()` for individual property reactivity
249
+ 5. **Use `Observable.value()`** to extract plain values from complex structures
250
+ 6. **Prefer computed** for derived values
251
+ 7. **Clean up** unused observables to prevent memory leaks
252
+ 8. **Use `trigger()`** when you need to force updates without value changes
253
+ 9. **Avoid** direct modifications in subscription callbacks
254
+
255
+ ## Next Steps
256
+
257
+ Now that you understand NativeDocument's observable, explore these advanced topics:
258
+
259
+ - **[Elements](docs/elements.md)** - Creating and composing UI
260
+ - **[Conditional Rendering](docs/conditional-rendering.md)** - Dynamic content
261
+ - **[Routing](docs/routing.md)** - Navigation and URL management
262
+ - **[State Management](docs/state-management.md)** - Global state patterns
263
+ - **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
264
+ - **[Memory Management](docs/memory-management.md)** - Memory management
265
+ - **[Anchor](docs/anchor.md)** - Anchor