Observable Objects
Inject shared, reactive objects into views so UI updates when the object changes.
ObservableObjects are shared objects whose fields notify observers. When those fields change, any observing view re-renders.
How It Works
- Extend
HsObservableto create a model class. - Decorate fields with
@observableFieldto 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.
Update observables from events or async handlers, not inside getViews().
Live Example
Increment/decrement updates the labels immediately. Toggling the name shows selective field observation.
⚙️ Key Props & Methods
- @observable([fields]): Observe a view field referencing an
HsObservable. - @observableField: Decorate fields inside your
HsObservablemodel to emit changes. - onObservableFieldUpdated(name, field?) => boolean: Return
falseto 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 extendingHsObservable. 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@observableFieldso 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';
}
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 byregisterObservableField(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.tshosanna-ui/src/hosanna-ui-examples/data/InjectObservableRig.tshosanna-ui/src/hosanna-ui-examples/data/SimpleObservable.ts