Skip to main content

Views, SubViews, and Children

Why This Matters

Hosanna uses a declarative view tree. You describe state and return view structs; the framework builds real views, wires parent/child relationships, and mounts native renderers.

Key Concepts

  • View: A virtual object with state that renders through a platform renderer (Roku SceneGraph, web DOM shim). Backed by BaseView<T>.
  • SubViews: Views created by a composite view’s getViews() result. They are declared children and tracked in subViews (flat list) and subViewsById.
  • Children: The on-screen mounted hierarchy. Managed via addChild/insertChild/removeChild and tracked in children and childrenById.
SubViews vs Children

SubViews are the declared structure from getViews(). Children are the mounted, on-screen structure. Most composite views have both.

How Views Are Built

  1. You update state and return structs from getViews().
  2. Hosanna inflates structs into views via buildView().
  3. Each view onMount() creates a renderer and becomes a child of its parent.
  4. IDs are resolved and stored for fast lookup.

Sequence

Build &amp; Mount Flow

IDs and Auto-Generation

  • You can set id on any struct. If omitted, Hosanna auto-generates hierarchical IDs based on position.
  • IDs are stable within a render pass and used for reconciliation and lookups.
Tip: Name Important Views

Assign explicit IDs to views you need to access later (focus, bounds, animations). Let others auto-generate.

How IDs Are Applied

  • During applyStateChanges, Hosanna calls applyIdsToViewStructs() to ensure each struct has a unique ID.
  • Auto IDs follow a parent-index pattern like __auto-0-2 when not specified.

Working with SubViews and Children

  • getSubView<T>(id, deep?) looks up declared subViews (optionally recursive).
  • getChildView<T>(id) returns mounted children by ID.
  • getSubViewRenderer<T>(id) returns a subView’s renderer if mounted.
Tip: Prefer SubView Lookups

Use getSubView for declaration-time relationships and getChildView when you need the mounted instance.

Generics and Safety

APIs accept generics so you get type-safe results:

const label = this.getSubView<Label>('title');
const grid = this.getChildView<GridGroup>('results');
const renderer = this.getSubViewRenderer<ISGNGroup>('thumbnail');
Caution: Nullability

Lookups can return undefined. Guard before use, especially during initial mount.

Class Relationships

Types and Relationships

Getting Views and Renderers

  • Views expose renderer?: R. It is present when mounted.
  • Use addSubView/insertSubView/removeSubView to manage declared subViews.
  • Use addChild/insertChild/removeChild to manage mounted hierarchy.
  • viewOwner links a subView back to its creator.
Where Do Renderers Come From?

onMount() calls createRenderer() and attaches it to the parent’s renderer. On web, renderers are fake SG nodes; on Roku they are SceneGraph nodes.

Focus and Layout Tips

  • Composite views act as focus groups and can set an initial focus via isInitialFocus on a child struct.
  • layoutChildren() positions mounted children using their translation, width, and height.
  • Use getBoundsInParent(view) or getBoundsInApp() when you need math-friendly rectangles.
Tip: Restore Focus Gracefully

Use setFocusedSubview(id) to hint where focus should land when children change.

Troubleshooting

  • If a lookup fails, verify the ID is actually present on the struct in the latest getViews() output.
  • Ensure the view is mounted before touching its renderer.
  • Prefer explicit IDs on interactive views to avoid auto-ID shifts when order changes.

⚙️ Key APIs

Key Methods
  • getSubView<T>(id, deep?): Find a declared subView by ID; deep search optional.
  • getChildView<T>(id): Find a mounted child by ID.
  • getSubViewRenderer<T>(id): Get a subView’s renderer.
  • addChild/insertChild/removeChild: Manage mounted hierarchy.
  • addSubView/insertSubView/removeSubView: Manage declared subViews.
Avoid Mutating During Render

Don’t change state inside getViews(). Compute structure only; set state before/after.