Observable Objects
Inject shared, reactive objects into views so UI updates when the object changes.
How It Works
@observable([fields])
marks a view field as an observable reference; changes to the listed fields trigger invalidation.@injectObservable('key', [fields])
injects an IoC-managed observable and subscribes to the listed fields.- Field filtering: pass a list of field names to observe only those. Defaults to
['__internalState']
(all changes). - Observable changes trigger
makeDirty(field)
→ view invalidation →getViews()
runs again. - Optionally override
onObservableFieldUpdated(observableFieldName, subField)
to intercept updates; return false to skip rerender.
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.
Annotations
@observable(['field1','field2'])
(on a view state field): marks that state field as an observable reference. The instance you assign should extendHsObservable
. The optional array narrows which underlying observable fields trigger invalidation.@injectObservable('serviceKey', ['field'])
(on a view state field): resolves anHsObservable
instance from IoC and observes only the listed fields. In examples weAppUtils.register('sharedObs', instance)
insidegetViews()
to keep it simple.- Observable model properties: decorate properties inside your model class (which must extend
HsObservable
) with@observableField
so changes notify observers.- API:
static/api/functions/common.decorators.observableField.html
.
- API:
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;
}
});
}
}
ObservableObjects
ObservableObjects in Hosanna UI are special objects whose properties can be observed for changes. When an observable property changes, any UI component or logic bound to it will automatically update, enabling reactive and declarative UI patterns.
Creating an ObservableObject
To make an object observable, use the @observable()
decorator on its properties or fields. Hosanna UI provides utilities to help you define and use observables easily.
Example:
import { observable } from 'hosanna-ui/lib/decorators';
export class SimpleObservable {
@observable() counter = 0;
@observable() name = 'John Doe';
}
Using ObservableObjects in Views
You can bind ObservableObjects to your views. When the observable's properties change, the UI will automatically reflect the new values.
Example:
import { view } from 'hosanna-ui/lib/decorators';
import { ObservableRigState } from './ObservableRig';
@view('ObservableRig')
export class ObservableRigView extends BaseExampleScreenView<ObservableRigState> {
@observable() myObservable = new SimpleObservable();
protected override getViews() {
return [
VGroup([
Label({ text: 'Counter: ' + this.myObservable.counter }),
Button({ text: 'Increment' }).onClick(() => this.myObservable.counter++),
Button({ text: 'Decrement' }).onClick(() => this.myObservable.counter--),
Button({ text: this.myObservable.name }).onClick(() => this.myObservable.name = ['John Doe', 'Jane Doe'][Math.floor(Math.random() * 2)]),
])
];
}
}
Passing Observables to Subviews
ObservableObjects can be passed as props to subviews, allowing for shared state and coordinated updates across components.
Example:
ObservableSubView({
myObservable: this.myObservable,
})
Injecting and Sharing Observables
You can register and inject observables using utilities like AppUtils.register
to share state between different parts of your app.
Example:
AppUtils.register('myObservable', this.myObservable);
this.present(InjectObservableRig());
Best Practices
- Use
@observable()
for any property that should trigger UI updates when changed. - Keep observable logic simple and focused on state that affects the UI.
- Pass observables to subviews for shared, reactive state.
- Use utilities like
AppUtils
for dependency injection and sharing observables.
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