Summary#
Some controls read a particular member off each row the app hands them — a canvas reads each node's position, a picture reads which node an edge leaves from. The app says WHICH member with a selector, the same lambda it writes in LINQ:
FlowCanvas(nodes: steps, edges: links, nodeTypes: types,
typeField: s => s.Kind, labelField: s => s.Title, edgeLabelField: e => e.Label);The compiler resolves each selector over the row type, so a typo is a compile error at the call — not a node drawn at
(0, 0) or an edge that never appears, which is what typeField: "Knd" did on a clean compile.
Signature#
control <Name><TRow> {
props {
TRow[] rows;
Func<TRow, double> xField = r => r.X; // a selector with a default: used when the row has that member
Func<TRow, string>? labelField; // optional: omitted means the control does without it
Func<TRow, object> keyField = r => r.Key; // `object`: any member (a row reference, or a key)
}
}Description#
What the call site may write#
ONE member of the row, read off the lambda's own parameter: s => s.X. The control's JavaScript reads that member
off the row it was handed, so:
| written | answer |
|---|---|
s => s.Left | the member Left |
s => s.Lfet | refused: class 'Step' has no property 'Lfet'. Did you mean 'Left'? (entity for an entity row) |
s => s.Title where a double is declared | refused: 'xField' selects a double, but Step.Title is string — with the members that ARE doubles |
s => s.Owner.Name | refused: one member, not a path — the row carries its own members only |
s => s.Title + "!" | refused: the control reads a member, so a computed value has nowhere to run |
"Left" | refused, with the selector to write and the members it could name |
The declared return type is checked: an enum where the control wants text is refused, because it would reach the control as its number and match nothing.
A control with a selector prop#
class Step { public string Title; public double Left; public double Top; }
control Board<TNode> {
contractVersion "1.1"
participation headless
props {
TNode[] nodes;
Func<TNode, double> xField = n => n.X;
Func<TNode, double> yField = n => n.Y;
Func<TNode, string>? labelField;
}
}
[Route("/board")] [AllowAnonymous]
component BoardPage() {
Step[] steps = [ new Step { Title = "Receive", Left = 40, Top = 60 },
new Step { Title = "Approve", Left = 340, Top = 40 } ];
render {
Board(nodes: steps, xField: s => s.Left, yField: s => s.Top, labelField: s => s.Title);
}
}What the control's JavaScript receives#
The member's KEY, as a string — props.xField is "Left", so the shim reads row[props.xField]. A selector prop
left out arrives as undefined, and the shim applies the declared default (props.xField ?? 'X'), exactly as for any
other defaulted prop. osy control build types it as xField?: string in the generated .control.d.ts.
DataGrid does not need one#
The kit's DataGrid is ordinary Osy#, so its columns RUN their selectors (Value = o => o.Reference, and computed
ones like f => Text.ByteSize(f.Size)). The order it opens in is said on the column too — Sort = UiSort.Descending — see Acting on data — a calm grid header, filters as chips, and a toolbar that follows the selection.
See also#
- control — foreign UI controls (charts, grids, maps) — the
controlblock, props and the shim - The flow kit — a node-and-edge canvas whose nodes are your own Osy# — every
…Fieldprop of the canvas is a selector - Laying a graph out — ranks, routed edges and a run's path on the flow canvas —
initialField/terminalFieldselect the starts and ends - Acting on data — a calm grid header, filters as chips, and a toolbar that follows the selection — the grid's starting order, on the column