Build Your First View Fragment
This tutorial builds one data-driven card that works both as a standalone
FragmentView and as a CollectionView cell.
You will:
- define the fragment tree in AppConfig
- bind item data to child fields
- add focused and normal status overlays
- mount the fragment in TypeScript
- update one mounted fragment without rebuilding the screen
1. Define the fragment
Add a reusable style under cells in assets/meta/app.config.json:
{
"cells": {
"featuredCard": {
"$supportsDataMap": true,
"width": 384,
"height": 260,
"views": {
"base": [
{
"id": "poster",
"subType": "Poster",
"uri": "${data.imageUrl}",
"width": 384,
"height": 216,
"opacity": 0.85,
"scale": [1, 1]
},
{
"id": "title",
"subType": "Label",
"text": "${data.title}",
"fontKey": "~theme.fonts.text-bold-20",
"color": "~theme.colors.white",
"translation": [0, 224],
"width": 384,
"height": 36
}
],
"normal": {
"poster": {
"opacity": 0.85,
"scale": [1, 1]
}
},
"focused": {
"poster": {
"opacity": 1,
"scale": [1.05, 1.05]
}
}
}
}
}
}
The important rules are:
$supportsDataMapenables${data.*}compilation.- Every bound or status-updated child has a stable, unique
id. - Bindings belong on fields in
views.base, not inside a status map. views.basecontains the correct initial normal appearance.views.normalrestores every field changed byviews.focused.
Do not add a root id to a fragment intended for repeated cells. The provider
generates unique fragment instance IDs when the root ID is absent.
2. Mount it with FragmentView
Use the AppConfig key as fragmentStyleKey and pass the binding object as
itemContent:
import { FragmentView } from '@hs-src/hosanna-ui/views/controls/FragmentView';
FragmentView({
id: 'featuredCard',
fragmentStyleKey: 'cells.featuredCard',
itemContent: {
title: 'Featured',
imageUrl: 'pkg:/images/featured-card.png',
},
canReceiveFocus: true,
});
FragmentView reads its initial width and height from the fragment style. You
can still set host dimensions explicitly when the surrounding layout owns the
size.
3. Update the mounted data
For an update that must remain the fragment's current item data, assign
itemContent on the mounted view:
import { FragmentViewView } from '@hs-src/hosanna-ui/views/controls/FragmentView';
const card = this.getSubView<FragmentViewView>('featuredCard');
if (card) {
card.itemContent = {
title: 'Updated title',
imageUrl: 'pkg:/images/featured-card-updated.png',
};
}
The fragment keeps its SG tree and updates the fields compiled from
${data.*}. This is cheaper than rebuilding the parent view.
updateData(data) is the lower-level optimized method when your application
already mutates and retains the same backing content object. It applies the
provided object immediately but does not replace the stored itemContent
state; a later status pass can reapply that stored object.
If the fragment uses data-dependent computed values without any constraints,
verify the result on your target. The current standalone FragmentView
implementation does not reliably rerun a computed-only map from a data update
unless a constraint pass is also present.
4. Reuse it in CollectionView
A CollectionView row selects the same fragment through cellSettingsKey:
{
"rows": {
"featured": {
"rowType": "HorizontalRow",
"height": 300,
"cellSize": [384, 260],
"cellSettingsKey": "cells.featuredCard"
}
}
}
Each row item becomes the fragment data object:
{
id: 'featured-1',
title: 'Featured',
imageUrl: 'pkg:/images/featured-card.png',
}
CollectionView owns cell focus, status changes, recycling, and fragment reuse. See Cells for row and mixed-cell selection.
5. Check the result
Before adding callbacks or constraints, confirm:
- both fields show item data rather than literal
${data.*}text - the first render already has the intended normal appearance
- focus applies the
focusedoverlay - leaving focus restores opacity and scale through
normal - repeated cards do not share a hard-coded fragment root ID
Continue with:
- Fragment Bindings for the complete binding grammar and limitations
- Fragment Computed Values for declarative derived values
- Fragment Constraints for child-to-child layout
- Fragment Callbacks and Lifecycle for imperative behavior and cleanup
- Fragment Performance and Debugging before enabling layout caching