A single Perspective label bound to view.custom.siteData cannot show every property of that object. A label renders one line of text, so binding it to a whole object gives you a serialized blob or nothing useful. To get one header-plus-value pair per property, generated automatically, you need a component that repeats a child view: a flex repeater fed by a transformed instances array, or a table with a cell render view that performs indirect tag binding from a tag path column. Both work. Pick based on where the values live and how the screen should look.
Which component should repeat the siteData properties?
The screen needs N header/value pairs, where N is the number of properties in siteData. Something has to multiply a template by N. On a working screen, a flex repeater usually does this: it takes an array and stamps one embedded view per element, sized to the number of rows in its source.
| Option | Where it is set | Effect on screen | Choose it when |
|---|---|---|---|
| Single label bound to the object | Label props.text
|
One line, not one per property | Never, for this layout |
| Flex repeater + child view | Repeater props.path, props.instances
|
Free-form tiles: header on top, value below, any styling | You want the card or tile look from the reference screens |
| Table + cell render view | Table props.data, column render set to view |
Grid rows, sorting and filtering built in | The source is a list of tag paths and a tabular layout is acceptable |
For the tile layout shown on the target screens, build the flex repeater first. The table route is covered further down because it handles tag-path lists with less scripting.
Check before moving on: open view.custom.siteData in the property editor and confirm it is an object with the property names you expect as headers. If it is an array or a dataset, the transform in the next step changes.
How do I turn view.custom.siteData into repeater instances?
A flex repeater does not iterate object keys. It iterates an array in props.instances, and each element's keys are passed to the child view as params with the same names. So convert the object into an array of {name, value} pairs.
- Select the flex repeater and add a Property binding on
props.instancespointing toview.custom.siteData. - Add a Script transform:
def transform(self, value, quality, timestamp): rows = [] for key in value.keys(): rows.append({"name": key, "value": value[key]}) return rows - If
siteDataholds tag paths rather than values, emit the path instead:{"name": key, "tagPath": value[key]}. The child view then resolves the live value itself (next sections). - Set
props.directiontorowfor side-by-side tiles orcolumnfor stacked rows.
Because the binding is live, any runtime write to siteData re-runs the transform and the repeater re-stamps its children.
Check: expand props.instances in the property editor. It should show one array element per siteData property, each with a name key and a value or tagPath key. If the array is empty, the transform is erroring. Look at the red binding indicator and the designer output console.
How does the child view receive the header and value?
Create the child view and point the repeater's props.path at it. Inside the child, declare input params whose names match the instance keys exactly:
| Child view param | Bound to | Label on screen |
|---|---|---|
name |
Header label props.text via property binding view.params.name
|
Property name shown as header |
value |
Value label props.text via property binding view.params.value
|
Static value from siteData
|
tagPath |
Indirect tag binding reference (next section) | Live tag value |
Key matching is case-sensitive. An instance key of Name will not populate a param called name. The param keeps its default, and every tile shows the same default text. When the operator sees identical headers on every tile, the repeater is working and the key names are wrong. When the operator sees no tiles at all, the repeater has no instances.
Check: in the designer, preview the parent view. Each tile should show a distinct header matching a siteData property name.
How do I make the value label follow a tag instead of a static value?
If the values have to track live tags, pass the path and let the child view bind indirectly. This keeps the tag subscription inside each child instance, so adding a property to siteData adds a subscribed tile with no extra configuration.
- On the value label, add a Tag binding and select Indirect.
- Enter the indirect path as
{path}, then add a reference namedpathbound toview.params.tagPath. - If only part of the path varies (for example, a site folder), build the rest of the string in the indirect path and substitute only the varying segment.
- Leave the binding unidirectional unless the operator needs to write from this label.
Separate tag faults from binding faults when the value label misbehaves:
| What the operator sees | Fault class | Cause |
|---|---|---|
| Bad-quality overlay on the value label | Tag | Path resolves to a missing tag, wrong provider, or disconnected device |
| Header correct, value blank with no overlay | Binding |
tagPath param empty; instance key misspelled or transform emitting value instead of tagPath
|
| All tiles show the same value | Binding | Label bound directly to one tag instead of through the {path} reference |
| Value correct in designer, stale in session | Binding | Instances built once by script instead of by a live binding on siteData
|
Check: hover the value label in preview and read the resolved path in the binding. Paste that path into the tag browser. If it resolves there but not in the label, the fault is in the binding. If it does not resolve, the fault is in the tag path data.
When is a table with a cell render view the better build?
When the source is a list of tag paths, with a dataset tag or an array of rows each carrying a tag path column, a table with a cell render view reaches the same result with less plumbing. The table already iterates rows. The render view only has to resolve one path per cell.
- Feed the table
props.datawith rows containing a header/name column and a tag path column. - In
props.columns, configure the tag path column's render as a view and point it at a small embedded view. - In that embedded view, declare the params the table passes into cell views (the cell
valueand the fullrowobject). Bind a label with an indirect tag binding whose reference is the param holding the tag path. - Keep the header column as plain text, or render it through the same view if it needs styling.
Pick the table when operators need sorting, filtering, or many rows in a fixed grid. Pick the flex repeater when the layout must match tile-style screens with the header above the value.
Check: preview the table and confirm each row's rendered cell shows a different live value. Then change one source tag and watch only that row update.
How do I prove the whole screen end to end?
- Launch a session (not only the designer preview) and count the tiles or rows. The count must equal the number of properties in
view.custom.siteData, or the number of rows in the source. - Confirm every header matches its property name, with no duplicates and no default param text.
- Write a new value to one source tag and confirm only its tile or row changes, within the tag's scan or subscription rate.
- Add a property to
siteData, or a row to the source dataset, at runtime. A new tile or row must appear without editing the view. This proves the binding is automated end to end. - Point one entry at a nonexistent path and confirm that tile alone shows the bad-quality overlay while the others stay good. That isolates tag faults per instance.
FAQ
What happens if the flex repeater instance keys don't match the child view param names?
The child params keep their default values, so every tile shows the same default header or value. Rename the keys in the script transform to match the param names exactly, including case.
What happens if view.custom.siteData changes while the session is running?
A property binding on props.instances re-evaluates, the script transform rebuilds the array, and the repeater re-renders the children. Instances written once from an event script will not update.
What happens if an indirect tag path in the repeater points to a tag that doesn't exist?
Only that instance's value label shows a bad-quality overlay; the other tiles are unaffected. Verify the path string, including the tag provider prefix, in the tag browser.
What happens if I feed a dataset tag to the flex repeater instead of an object?
The repeater needs an array of objects, so the transform has to loop over the dataset rows and build one dictionary per row. Alternatively, bind the dataset to a table and use a cell render view for the tag path column.
How do I control the order of headers in a Perspective flex repeater?
Do not rely on object key order. Iterate an explicit list of property names, or sort the keys, inside the script transform before building the instances array.