native-document 1.0.9 → 1.0.11
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/native-document.dev.js +2671 -0
- package/dist/native-document.min.js +1 -0
- package/docs/anchor.md +208 -0
- package/docs/conditional-rendering.md +628 -0
- package/docs/contributing.md +51 -0
- package/docs/core-concepts.md +513 -0
- package/docs/elements.md +383 -0
- package/docs/getting-started.md +403 -0
- package/docs/lifecycle-events.md +106 -0
- package/docs/memory-management.md +90 -0
- package/docs/observables.md +265 -0
- package/docs/routing.md +817 -0
- package/docs/state-management.md +423 -0
- package/docs/validation.md +193 -0
- package/elements.js +3 -1
- package/index.js +2 -0
- package/package.json +1 -1
- package/readme.md +189 -425
- package/router.js +2 -0
- package/src/data/MemoryManager.js +15 -5
- package/src/data/Observable.js +35 -2
- package/src/data/ObservableChecker.js +3 -0
- package/src/data/ObservableItem.js +4 -0
- package/src/data/Store.js +6 -6
- package/src/router/Router.js +13 -13
- package/src/router/link.js +8 -5
- package/src/utils/plugins-manager.js +12 -0
- package/src/utils/prototypes.js +13 -0
- package/src/utils/validator.js +23 -1
- package/src/wrappers/AttributesWrapper.js +11 -1
- package/src/wrappers/HtmlElementWrapper.js +10 -1
|
@@ -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
|