Skip to content

Base Observable

Mubarrat edited this page Aug 8, 2025 · 1 revision

Interface: baseObservable<T = any>

A reactive primitive designed for managing and observing mutable state with built-in event dispatching and flexible binding modes.

Overview

baseObservable is a callable object that acts as both a getter and setter for a reactive value of type T. It implements the EventTarget interface for notifying subscribers about changes, supports binding mode configurations, and offers advanced features such as validation, coercion, and derived observables via a binding proxy.


Type Parameters

Parameter Description
T The type of the observable's managed value

Properties

_eventTarget: EventTarget

Internal instance responsible for event dispatch and listener management. Used to implement EventTarget methods.


type: "to" | "from" | "two-way"

Defines the data flow direction:

Mode Description
"to" One-way: ViewModel → UI (output only)
"from" One-way: UI → ViewModel (input only)
"two-way" Two-way: bidirectional synchronization

bind: (T extends object | Function ? {...} : {}) & {...}

A proxy object that exposes:

  • Properties of the underlying value as individual observables (computed<T[K]>).
  • Methods of the underlying value as functions returning computed observables.
  • A select<U>(selector: (value: T) => U): computed<U> method for derived observables.

Example:

const person = baseObservable(() => ({ name: "Alice", age: 30 }));
const nameObs = person.bind.name;       // observable for name property
const agePlusOne = person.bind.select(p => p.age + 1);

Callable Signature

(...args: any[]) => T
  • Getter: Called without arguments, returns current value.
  • Setter: Called with arguments, updates or recomputes value.

Methods

notifyBefore(change?: object): boolean

  • Dispatches a "valuechanging" event before the value update.
  • Returns false if any event listener cancels the change via preventDefault().
  • Allows validation or veto logic.

notify(change?: object): void

  • Dispatches a "valuechanged" event after the value update.
  • Notifies subscribers of the completed change.

tryChange<TResult>(fn: () => TResult, change?: object): TResult | undefined

Performs an atomic update:

  • Calls notifyBefore to allow cancellation.
  • Runs mutation function fn if not canceled.
  • Calls notify after mutation.
  • Returns result from fn or undefined if canceled.

validatable(validator?: (val: T) => boolean): this & { isValid: computed<boolean> }

  • Adds reactive validation support.
  • isValid is a computed observable reflecting validity.
  • Default validator always returns true.

coercible(coerce?: (...args: any[]) => any): this

  • Adds coercion to input arguments before setting.
  • Useful for input normalization or type enforcement.

Interface: baseObservableConstructor

Constructor and static helper methods for baseObservable.


Constructor Signature

<T = any>(baseFunction: (...args: any[]) => T): baseObservable<T>;

Creates a baseObservable wrapping the supplied baseFunction.


Static Methods

autoBind<T>(observable: baseObservable<T> | T, set: (val: T) => void, observe?: (val: T) => void): void

Establishes a binding between a source (observable or plain value) and a setter:

  • Immediately calls set with current value.
  • Subscribes to "valuechanged" if observable supports output binding.
  • Calls observe for input binding if supported.

Usage Example

const count = baseObservable(() => 0);
count.type = "two-way";

// Subscribe to changes
count._eventTarget.addEventListener("valuechanged", () => {
    console.log("Count changed:", count());
});

// Update count
count(5);

// Extend with validation
const validatedCount = count.validatable(val => val >= 0);
console.log(validatedCount.isValid()); // true or false

// Coerce input (force integer)
count.coercible(val => Math.floor(val));
count(3.7);
console.log(count()); // Outputs: 3

Clone this wiki locally