Skip to main content

Observable Objects

An HsObservable publishes changes to its decorated fields. A BaseView can observe the object and rebuild when those fields change.

Define an observable

An observable field change updates state and version, synchronously notifies matching handlers, and schedules observing views for their next updateAn observable field change updates state and version, synchronously notifies matching handlers, and schedules observing views for their next update

Extend HsObservable and decorate each published property:

import { HsObservable } from '@hs-src/hosanna-ui/hosanna-api';
import { observableField } from '@hs-src/hosanna-ui/lib/decorators';

export class CounterModel extends HsObservable {
@observableField
count = 0;

@observableField
name = 'John Doe';
}

Assignments notify only when the value changes. Use @observableField(true) only when duplicate assignments must also notify.

Observe a view property

Use @observable() for an object owned by or passed into a view:

export class CounterScreenView extends BaseView<CounterScreenState> {
@observable()
counter = new CounterModel();

protected override getViews() {
return [
Label({ text: `Count: ${this.counter.count}` }),
Button({ text: 'Increment' })
.onClick(() => this.counter.count++),
];
}
}

Pass a field list to limit invalidation:

@observable(['count'])
counter = new CounterModel();

With no list, the decorator observes __internalState, which receives changes from all decorated model fields.

Do not mutate during render

Change observable fields from input handlers, lifecycle methods, or async completion callbacks—not from getViews().

Inject a shared observable

Register the instance, then use the supported @injectObservable() decorator:

AppUtils.register('counter', counter);
export class SummaryScreenView extends BaseView<SummaryScreenState> {
@injectObservable('counter', ['count'])
counter!: CounterModel;
}

@injectObservable both resolves the IoC key and wires observation. Use @observable() instead when the object arrives as view state or is constructed by the view.

Filter an update in a view

A view can inspect observable notifications before Hosanna invalidates it:

protected override onObservableFieldUpdated(
observableFieldName: string,
field?: string,
): boolean {
if (observableFieldName === 'counter' && field === 'name') {
return false;
}
return true;
}

Return false only when that update cannot affect the rendered structure.

Manual observation

Prefer the decorators for normal view rendering. Lower-level integrations can call addObserver() and removeObserver() directly—for example, when an adapter updates a native renderer without rebuilding the view. Always remove a manual observer when its owner stops listening.

Talk to us