# Cell template (your own content in a control's cell)

> A control that paints cells — a grid — writes plain text in each one. A `slot <Field> { row => … }` block on the call replaces that column's cell with your own content: a status pill, an avatar, a link, a button. The control keeps the columns, the sorting and the responsive layout; you decide what a cell looks like.

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

## Summary        {#summary}
A grid renders each cell as text. That is right for a name or a price, and wrong for a **status** — which wants a
coloured pill — or a person, which wants an avatar.

A **cell template** is how you take a column over. Inside the control's call, write `slot <Field> { row => … }` and
that column's cells render *your* content instead of text. Everything else about the grid still works: the column
still sorts, the table still becomes cards on a narrow screen, the row still raises its selection event.

## Signature      {#signature}
```osy syntax
DataGrid(rows: <list>, columns: [ … ]) {
  slot <Field> { <row> => <content> }     // <Field> is a property of the row; <row> binds that row
}
```

`<Field>` names a property of the row type. If it names something that is not a property — a typo, a column you
renamed — that is a **compile error**, not a cell that quietly renders nothing.

## Description    {#description}

### A status pill   {#pill}
The classic case: the status column should be a pill, tinted by what the status *is*.

```osy title="status as a pill" test app=ui-cell-template
enum OrgStatus {
  Active,
  [Label("Suspended")] Suspended,
}

entity Organization {
  [Required, MaxLength(100)] string Name;
  OrgStatus Status = OrgStatus.Active;
}

class GridColumn { public string Key; public string Label; }

control DataGrid<T> {
  contractVersion "1.0"
  participation headless
  props {
    T[] rows;
    GridColumn[] columns;
  }
  events { rowSelected(T row); }
}

theme Admin {
  Colors {
    Surface1      = "#fbfbfa";
    TextSecondary = "#5f5f5a";
    BgSuccess     = "#eaf3de";
    TextSuccess   = "#3b6d11";
    BgWarning     = "#faeeda";
    TextWarning   = "#854f0b";
  }
  Radius { Pill = "999px"; }
}

enum Tone { Neutral, Success, Warning }

/// The pill itself — an ordinary component. Its label is CONTENT, so it can hold an enum-typed field, which renders
/// as that member's label.
[Composable] component Badge(Tone tone) {
  variants {
    base { Display = Display.InlineFlex; Px = "10px"; Rounded = Radius.Pill; }
    tone {
      Neutral { Bg = Colors.Surface1;  Color = Colors.TextSecondary; }
      Success { Bg = Colors.BgSuccess; Color = Colors.TextSuccess; }
      Warning { Bg = Colors.BgWarning; Color = Colors.TextWarning; }
    }
  }
  render { Row(align: Align.Center) { Slot; } }
}

[Page("/orgs")]
[Render(CSR)]
component OrganizationsPage() {
  var orgs = Organization.ToList();

  render {
    DataGrid(rows: orgs, columns: [
      new GridColumn { Key = "Name", Label = "Organization" },
      new GridColumn { Key = "Status", Label = "Status" }
    ]) {
      slot Status { o =>
        Row(align: Align.Center) {
          if (o.Status == OrgStatus.Active) { Badge(Tone.Success) { Text(o.Status); } }
          else { Badge(Tone.Warning) { Text(o.Status); } }
        }
      }
    }
  }
}
```

Three things are worth naming, because each is a decision you are making:

- **The pill is yours.** It is an ordinary component, not something the grid provides. Restyle it, or replace it with
  something else entirely, and no control has to know.
- **The colour is a UI decision, so it lives in the UI.** `Active` is green because *this screen* says so. Nothing
  about the colour belongs on the enum — an enum is a set of values, not a palette.
- **`Text(o.Status)` shows the member's label**, because it reads an enum-typed field directly. See [[Label], [Icon], [Tone] — what a human reads](https://osysharp.com/reference/enum/labels/).

### What the `{ o => … }` parameter binds to   {#row}
`{ o => … }` binds that row, typed as the row's entity — so `o.Status`, `o.Name` and any other property type-check.
You can read whatever the row has, not just the column you are rendering:

```osy syntax
slot Name { o => Row(gap: 2) { Avatar(o.AvatarUrl); Text(o.Name); } }
```

### Columns you do not template are unchanged   {#untouched}
Only the columns you write a `slot` for change. Everything else keeps rendering as text, with the alignment and
formatting the column declared. Start by templating the one column that needs it.

## See also       {#see-also}
- [control — foreign UI controls (charts, grids, maps)](https://osysharp.com/reference/ui/controls/) — the controls a cell template applies to, and how a control is declared
- [[Label], [Icon], [Tone] — what a human reads](https://osysharp.com/reference/enum/labels/) — why `Text(o.Status)` reads as "Suspended" and not a stored number
- [Slot (child content)](https://osysharp.com/reference/ui/slots/) — the `slot` construct in its other role: filling a component's named slot
