Skip to main content

Observable Objects

Inject shared, reactive objects into views so UI updates when the object changes.

Overview

ObservableObjects are shared objects whose fields notify observers. When those fields change, any observing view re-renders.

Observables Flow

How It Works

  • Extend HsObservable to create a model class.
  • Decorate fields with @observableField to emit changes.
  • (if this model is used in multiple views, you can use AppUtils.register(key, instance), or add it to the IOCContainer, in your AppInitializer to register it in IoC and resolve it where needed)
  • Ensure your view knows the model is an observable field by using @observable yourField: SomeObservable, for passed in state, or @injectObservable yourField: SomeObservable, for injected observable models.
  • Field filtering: pass a list of field names to observe only those. Defaults to ['__internalState'] (all changes), this makes your app more efficient.
  • Optionally override onObservableFieldUpdated(observableFieldName, subField) to intercept updates in your view; return false to skip re-renderring on this change.
Don’t Mutate During Render

Update observables from events or async handlers, not inside getViews().

Live Example

Try It

Increment/decrement updates the labels immediately. Toggling the name shows selective field observation.

⚙️ Key Props & Methods

Key Props
  • @observable([fields]): Observe a view field referencing an HsObservable.
  • @observableField: Decorate fields inside your HsObservable model to emit changes.
  • onObservableFieldUpdated(name, field?) => boolean: Return false to suppress a re-render.
  • AppUtils.register(key, instance): Register a shared observable in IoC.
  • AppInitializer can add services to the IoCContainer, or use AppUtils.register(key, instance) to register them.

Implementation Details

  • @observable(['field1','field2']) (on a view state field): mark a state field as an observable reference. Assign an instance extending HsObservable. The optional array narrows which underlying observable fields trigger invalidation.
  • To inject from IoC, call this.getObservableField('myObservable', 'serviceKey'); it resolves via IoC and wires observers based on your decorator filter.
  • Model properties: decorate properties inside your model class (which must extend HsObservable) with @observableField so changes notify observers.
import { HsObservable } from 'hosanna-ui/hosanna-api';
import { observableField } from 'hosanna-ui/lib/decorators';

export class SimpleObservable extends HsObservable {
@observableField counter = 0;
@observableField name = 'John Doe';
}
No @injectObservable Decorator

The @injectObservable decorator is not available. Use getObservableField(field, 'key') with AppUtils.register('key', instance) instead.

BaseView Hooks

  • protected onObservableFieldUpdated(observableFieldName: string, field?: string): boolean — return false to prevent re-render for a specific update.
  • Observers are attached via updateObservable(fieldName, value) using filters registered by registerObservableField (done by the decorators).

Managing Observers Manually

In specialized cases, add/remove observers yourself. For example, the video player toggles an observer on shared player state:

private toggleVideoPlayerStateObserver(active: boolean): void {
if (this.videoPlayerState) {
this.videoPlayerState.removeObserver('__internalState', this);
}
if (active) {
this.videoPlayerState.addObserver('__internalState', this, (field) => {
switch (field) {
case 'control':
this.renderer.control = this.videoPlayerState.control;
break;
case 'seek':
this.renderer.seek = this.videoPlayerState.seek;
break;
}
});
}
}

Passing Observables to Subviews

ObservableObjects can be passed as props to subviews for shared, reactive state.

ObservableSubView({
myObservable: this.myObservable,
})

Injecting and Sharing Observables

Register shared instances in IoC and resolve where needed.

AppUtils.register('myObservable', this.myObservable);
this.getObservableField('myObservable', 'myObservable'); // resolves and wires observers

Best Practices

  • Use @observable() on any view field that should re-render when the model changes.
  • Keep models focused on UI-relevant state; avoid heavy logic in views.
  • Pass observables to subviews to coordinate updates.
  • Prefer IoC for cross-screen/shared state; avoid global singletons.

See Also

  • hosanna-ui/src/hosanna-ui-examples/data/ObservableRig.ts
  • hosanna-ui/src/hosanna-ui-examples/data/InjectObservableRig.ts
  • hosanna-ui/src/hosanna-ui-examples/data/SimpleObservable.ts