0
# Observable System
1
2
Core reactive data binding system providing observable values, computed observables, and observable arrays for automatic UI synchronization. The observable system is the foundation of Knockout.js, enabling two-way data binding and automatic UI updates.
3
4
## Capabilities
5
6
### Observable Creation
7
8
Creates observable values that can be read, written, and subscribed to for change notifications.
9
10
```javascript { .api }
11
/**
12
* Creates an observable value
13
* @param initialValue - The initial value (optional)
14
* @returns Observable instance
15
*/
16
function observable<T>(initialValue?: T): Observable<T>;
17
18
/**
19
* Creates an observable array with array manipulation methods
20
* @param initialItems - Initial array items (optional)
21
* @returns ObservableArray instance
22
*/
23
function observableArray<T>(initialItems?: T[]): ObservableArray<T>;
24
```
25
26
**Usage Examples:**
27
28
```javascript
29
import ko from "knockout";
30
31
// Basic observable
32
const name = ko.observable("John");
33
console.log(name()); // "John"
34
name("Jane");
35
console.log(name()); // "Jane"
36
37
// Observable with no initial value
38
const age = ko.observable();
39
age(25);
40
41
// Observable array
42
const items = ko.observableArray(["apple", "banana"]);
43
items.push("orange");
44
console.log(items()); // ["apple", "banana", "orange"]
45
```
46
47
### Computed Observables
48
49
Creates computed observables that automatically recalculate when their dependencies change.
50
51
```javascript { .api }
52
/**
53
* Creates a computed observable that derives its value from other observables
54
* @param evaluator - Function that returns the computed value
55
* @param evaluatorTarget - Optional 'this' context for evaluator
56
* @param options - Optional configuration
57
* @returns Computed observable
58
*/
59
function computed<T>(evaluator: () => T): Computed<T>;
60
function computed<T>(evaluator: () => T, evaluatorTarget: any): Computed<T>;
61
function computed<T>(options: ComputedOptions<T>): Computed<T>;
62
63
/**
64
* Creates a pure computed observable with optimized behavior
65
* @param evaluator - Function that returns the computed value
66
* @returns PureComputed observable
67
*/
68
function pureComputed<T>(evaluator: () => T): PureComputed<T>;
69
function pureComputed<T>(options: ComputedOptions<T>): PureComputed<T>;
70
```
71
72
**Usage Examples:**
73
74
```javascript
75
import ko from "knockout";
76
77
const firstName = ko.observable("John");
78
const lastName = ko.observable("Doe");
79
80
// Basic computed
81
const fullName = ko.computed(() => {
82
return firstName() + " " + lastName();
83
});
84
85
// Computed with write function
86
const fullNameWritable = ko.computed({
87
read: () => firstName() + " " + lastName(),
88
write: (value) => {
89
const parts = value.split(" ");
90
firstName(parts[0] || "");
91
lastName(parts[1] || "");
92
}
93
});
94
95
// Pure computed (optimized for better performance)
96
const displayName = ko.pureComputed(() => {
97
return firstName() + " " + lastName();
98
});
99
```
100
101
### Observable Interface
102
103
Core interface for all observable types with read/write and subscription capabilities.
104
105
```typescript { .api }
106
interface Observable<T> {
107
/** Read the current value */
108
(): T;
109
/** Write a new value and return any for API consistency */
110
(value: T): any;
111
112
/** Subscribe to value changes */
113
subscribe(callback: (newValue: T) => void): Subscription;
114
subscribe(callback: (newValue: T) => void, target: any): Subscription;
115
subscribe(callback: (newValue: T) => void, target: any, event: string): Subscription;
116
117
/** Read value without creating a dependency */
118
peek(): T;
119
120
/** Manually notify subscribers that the value has changed */
121
valueHasMutated(): void;
122
123
/** Notify subscribers that the value is about to change */
124
valueWillMutate(): void;
125
126
/** Extend the observable with additional behavior */
127
extend(extenders: ExtenderOptions): Observable<T>;
128
129
/** Get the number of active subscriptions */
130
getSubscriptionsCount(): number;
131
}
132
133
interface Subscription {
134
/** Remove the subscription */
135
dispose(): void;
136
/** Remove subscription when DOM node is removed */
137
disposeWhenNodeIsRemoved(node: Node): void;
138
}
139
```
140
141
### Observable Array Interface
142
143
Extended observable interface for arrays with array manipulation methods.
144
145
```typescript { .api }
146
interface ObservableArray<T> extends Observable<T[]> {
147
// Standard array methods
148
/** Add items to the end of the array */
149
push(...items: T[]): number;
150
/** Remove and return the last item */
151
pop(): T | undefined;
152
/** Remove and return the first item */
153
shift(): T | undefined;
154
/** Add items to the beginning of the array */
155
unshift(...items: T[]): number;
156
/** Remove and insert items at specified position */
157
splice(start: number, deleteCount?: number, ...items: T[]): T[];
158
/** Get a portion of the array */
159
slice(start?: number, end?: number): T[];
160
/** Reverse the array in place */
161
reverse(): ObservableArray<T>;
162
/** Sort the array in place */
163
sort(compareFunction?: (a: T, b: T) => number): ObservableArray<T>;
164
165
// Knockout-specific methods
166
/** Get the index of an item */
167
indexOf(item: T): number;
168
/** Replace an item with another */
169
replace(oldItem: T, newItem: T): void;
170
/** Remove specific items and return them */
171
remove(item: T): T[];
172
remove(predicate: (item: T) => boolean): T[];
173
/** Remove all items or specific items */
174
removeAll(): T[];
175
removeAll(items: T[]): T[];
176
/** Mark items as destroyed (adds _destroy: true property) */
177
destroy(item: T): void;
178
destroy(predicate: (item: T) => boolean): void;
179
/** Mark all items as destroyed */
180
destroyAll(): void;
181
destroyAll(items: T[]): void;
182
183
// Non-mutating methods
184
/** Get a sorted copy of the array */
185
sorted(compareFunction?: (a: T, b: T) => number): T[];
186
/** Get a reversed copy of the array */
187
reversed(): T[];
188
}
189
```
190
191
### Computed Observable Interface
192
193
Extended observable interface for computed values with dependency tracking.
194
195
```typescript { .api }
196
interface Computed<T> extends Observable<T> {
197
/** Dispose the computed and clean up dependencies */
198
dispose(): void;
199
/** Check if the computed is currently active */
200
isActive(): boolean;
201
/** Get the number of dependencies */
202
getDependenciesCount(): number;
203
/** Get array of dependent observables */
204
getDependencies(): Observable<any>[];
205
}
206
207
interface PureComputed<T> extends Computed<T> {
208
// Pure computeds have the same interface but optimized behavior
209
}
210
211
interface ComputedOptions<T> {
212
/** Function to compute the value */
213
read?: () => T;
214
/** Function to handle writes (makes computed writable) */
215
write?: (value: T) => void;
216
/** Context for read/write functions */
217
owner?: any;
218
/** Whether this is a pure computed */
219
pure?: boolean;
220
/** Defer evaluation until first access */
221
deferEvaluation?: boolean;
222
/** Dispose when this DOM node is removed */
223
disposeWhenNodeIsRemoved?: Node;
224
/** Function that returns true when computed should be disposed */
225
disposeWhen?: () => boolean;
226
}
227
```
228
229
### Extenders
230
231
Observable extension system for adding additional behavior like throttling and rate limiting.
232
233
```javascript { .api }
234
const extenders: {
235
/** Throttle observable notifications */
236
throttle: (observable: Observable<any>, timeout: number) => Observable<any>;
237
/** Rate limit observable notifications */
238
rateLimit: (observable: Observable<any>, options: RateLimitOptions) => Observable<any>;
239
/** Defer observable notifications */
240
deferred: (observable: Observable<any>, value: true) => Observable<any>;
241
/** Control notification behavior */
242
notify: (observable: Observable<any>, value: "always" | any) => Observable<any>;
243
/** Track array changes for observable arrays */
244
trackArrayChanges: (observable: ObservableArray<any>, value: true) => ObservableArray<any>;
245
};
246
```
247
248
```typescript { .api }
249
interface RateLimitOptions {
250
timeout: number;
251
method?: "notifyAtFixedRate" | "notifyWhenChangesStop";
252
}
253
254
interface ExtenderOptions {
255
throttle?: number;
256
rateLimit?: number | RateLimitOptions;
257
deferred?: true;
258
notify?: "always" | any;
259
trackArrayChanges?: true;
260
}
261
```
262
263
**Usage Examples:**
264
265
```javascript
266
import ko from "knockout";
267
268
// Throttle updates
269
const throttledValue = ko.observable("").extend({ throttle: 500 });
270
271
// Rate limit updates
272
const rateLimitedValue = ko.observable("").extend({
273
rateLimit: { timeout: 500, method: "notifyWhenChangesStop" }
274
});
275
276
// Defer updates
277
const deferredValue = ko.observable("").extend({ deferred: true });
278
```
279
280
### Dependency Tracking
281
282
Control dependency tracking behavior in computed observables and subscriptions.
283
284
```javascript { .api }
285
/**
286
* Execute function without creating dependencies
287
* @param callback - Function to execute
288
* @param callbackTarget - Optional 'this' context
289
* @param callbackArgs - Optional arguments array
290
* @returns Result of callback
291
*/
292
function ignoreDependencies<T>(
293
callback: () => T,
294
callbackTarget?: any,
295
callbackArgs?: any[]
296
): T;
297
298
/**
299
* Information about current computed context
300
*/
301
const computedContext: {
302
/** Get number of dependencies in current context */
303
getDependenciesCount(): number;
304
/** Get array of dependencies in current context */
305
getDependencies(): Observable<any>[];
306
/** Check if this is the initial evaluation */
307
isInitial(): boolean;
308
/** Register a dependency in current context */
309
registerDependency(observable: Observable<any>): void;
310
};
311
```
312
313
**Usage Examples:**
314
315
```javascript
316
import ko from "knockout";
317
318
// Access current computed context inside a computed
319
const computed = ko.computed(() => {
320
const context = ko.computedContext;
321
console.log("Dependencies:", context.getDependenciesCount());
322
console.log("Is initial run:", context.isInitial());
323
return someValue();
324
});
325
326
// Execute code without dependency tracking
327
const result = ko.ignoreDependencies(() => {
328
// This won't create dependencies even inside a computed
329
return someObservable() + anotherObservable();
330
});
331
```
332
333
### Observable Utilities
334
335
Utility functions for working with observables and managing async observable patterns.
336
337
```javascript { .api }
338
/**
339
* Creates a promise that resolves when a predicate becomes true
340
* @param predicate - Function returning boolean or observable boolean
341
* @returns Promise that resolves when predicate is true
342
*/
343
function when<T>(predicate: () => T): Promise<T>;
344
345
/**
346
* Subscribe to a predicate with callback (alternative to promise)
347
* @param predicate - Function returning boolean or observable boolean
348
* @param callback - Callback called when predicate becomes true
349
* @returns Subscription that can be disposed
350
*/
351
function when<T>(predicate: () => T, callback: (value: T) => void): Subscription;
352
```
353
354
**Usage Examples:**
355
356
```javascript
357
import ko from "knockout";
358
359
const isReady = ko.observable(false);
360
const data = ko.observable(null);
361
362
// Promise-based usage
363
ko.when(() => isReady()).then(() => {
364
console.log("System is ready!");
365
});
366
367
// Callback-based usage
368
const subscription = ko.when(() => data(), (value) => {
369
console.log("Data received:", value);
370
});
371
372
// Wait for complex conditions
373
ko.when(() => {
374
return user.isLoaded() && permissions.isLoaded() && settings.isLoaded();
375
}).then(() => {
376
console.log("All data loaded, initializing app");
377
});
378
```
379
380
### Testing Functions
381
382
Type checking functions for observable objects.
383
384
```javascript { .api }
385
/**
386
* Test if object is an observable
387
* @param obj - Object to test
388
* @returns True if object is observable
389
*/
390
function isObservable(obj: any): boolean;
391
392
/**
393
* Test if object is an observable array
394
* @param obj - Object to test
395
* @returns True if object is observable array
396
*/
397
function isObservableArray(obj: any): boolean;
398
399
/**
400
* Test if object is a computed observable
401
* @param obj - Object to test
402
* @returns True if object is computed
403
*/
404
function isComputed(obj: any): boolean;
405
406
/**
407
* Test if object is a pure computed observable
408
* @param obj - Object to test
409
* @returns True if object is pure computed
410
*/
411
function isPureComputed(obj: any): boolean;
412
413
/**
414
* Test if object is subscribable
415
* @param obj - Object to test
416
* @returns True if object is subscribable
417
*/
418
function isSubscribable(obj: any): boolean;
419
420
/**
421
* Test if object is a writeable observable
422
* @param obj - Object to test
423
* @returns True if object is writeable observable
424
*/
425
function isWriteableObservable(obj: any): boolean;
426
427
/**
428
* Alternative spelling for isWriteableObservable
429
* @param obj - Object to test
430
* @returns True if object is writeable observable
431
*/
432
function isWritableObservable(obj: any): boolean;
433
```