# Member selectors — a control prop that names a member of your row: xField: s => s.X

> A control prop declared Func<TRow, V> is a member selector: the call site writes `xField: s => s.X`, the compiler checks it against the row type the call binds, and the control receives that member's key. A misspelt member is a compile error naming the member and the type, where a string used to draw nothing.

<!-- id: ui-control-selectors · area: ui · stability: stable · html: https://osysharp.com/reference/ui/control-selectors/ -->

## Summary        {#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:

```osy syntax
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      {#signature}
```osy syntax
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    {#description}

### What the call site may write   {#call-site}

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   {#example}

```osy title="a canvas that positions each row by the member the page names" test app=ui-control-selectors
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   {#shim}

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   {#data-grid}

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](https://osysharp.com/reference/ui/selection/).

## See also       {#see-also}
- [control — foreign UI controls (charts, grids, maps)](https://osysharp.com/reference/ui/controls/) — the `control` block, props and the shim
- [The flow kit — a node-and-edge canvas whose nodes are your own Osy#](https://osysharp.com/reference/ui/flow-kit/) — every `…Field` prop of the canvas is a selector
- [Laying a graph out — ranks, routed edges and a run's path on the flow canvas](https://osysharp.com/reference/ui/flow-layout/) — `initialField` / `terminalField` select the starts and ends
- [Acting on data — a calm grid header, filters as chips, and a toolbar that follows the selection](https://osysharp.com/reference/ui/selection/) — the grid's starting order, on the column
