# A picker over every row — the SearchPicker

> A Dropdown is usable for a few dozen options; over an entity that grows, past the first answer (about 2 MB of rows) the rows after it are not offered at all. SearchPicker is the control for it: the person types, the page's own query reads the few rows that match on the server, and the person picks one. osy lint's ui-picker-over-a-growing-entity (SHOULD) fires on a dropdown or picker over every row of an entity and names it.

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

## Summary        {#summary}

`SearchPicker` chooses one row of a table too big to list. The text the person types is a field of the page, the
page's query reads it, so each keystroke is answered by the **database, over every row** — the page never loads the
table to filter it. The match is offered in a panel under the box; picking one sets the value and shows it.

```osy title="type a few letters, choose from the twenty that match" test app=ui-search-pickers
entity Vendor {
  [Required, MaxLength(80)] string Name;
  security { allow read, create when IsAnonymous || IsAuthenticated; }
}

[Route("/pick")] [Render(CSR)] [AllowAnonymous]
component PickVendor() {
  string find = "";
  Vendor? vendor = null;
  live var matches = Vendor.Where(v => v.Name.Contains(find)).OrderBy(v => v.Name).Take(20).ToList();

  render {
    Stack(gap: 2, p: 6) {
      SearchPicker("Vendor", value: vendor, search: find, options: matches) { v => Text(v.Name); }
      if (vendor != null) { Text("Buying from " + vendor.Name); }
    }
  }
}
```

## Signature      {#signature}

```osy syntax
string find = "";
live var matches = Entity.Where(x => x.Name.Contains(find)).OrderBy(x => x.Name).Take(20).ToList();
SearchPicker("…", value: chosen, search: find, options: matches) { x => Text(x.Name); }
// SearchPicker<T>(string label, Binding<T> value, Binding<string> search, T[] options,
//                 string placeholder = "Type to search", bool disabled = false)
```

## Description    {#description}

### How it behaves   {#behaviour}

The panel opens on what was typed, never on an empty box. It lists the page's matches with the template's row, says
**"Nothing matches …"** when there are none, and — when the page reads with no `.Take` and more rows match than one
answer carries — **"More match — keep typing to narrow it."** Picking a row sets `value`, clears the search and shows
the row; pressing it searches again. Esc clears the search. `value` is a two-way binding, `T?` for an optional choice,
as on `Dropdown`. In a test, fill it by its label and press the match: `Ui.Fill("Vendor", "acm")`, then
`Ui.Click("Acme Ltd")`.

`Dropdown` stays the control for an enum, or for a handful of rows the page already holds.

### When does the lint fire?   {#fires}

`ui-picker-over-a-growing-entity` is a **SHOULD** finding on a component field read from an entity with no `Take` whose
rows are handed to a choice control — `Dropdown`, `Select`, `Combobox`, `RadioGroup`, or any control named `…Picker`,
`SearchPicker` included — as its options or written inside it. Past a few dozen nobody finds an option by scrolling,
and past the first answer (about 2 MB of rows, [How many rows a page is sent](https://osysharp.com/reference/ui/read-limits/)) the rows after it are not offered at all — the one
being looked for is simply missing.

### When is it silent?   {#silent}

When the choices are an **enum's members** (`Dropdown("Status", value: s)` over `T.Members` is not a query), or the
read is bounded: a `Take`, a one-row read, an aggregate, or a child collection pinned to one parent.

### What can it not tell?   {#limits}

Whether the entity is reference data that never grows — a dozen currencies, the countries. The source does not say.
Write `.Take(n)` naming its size: it says what the page means and silences the finding.

## See also        {#see-also}

- [How many rows a page is sent](https://osysharp.com/reference/ui/read-limits/) — what a page is sent
- [Lists that load more](https://osysharp.com/reference/ui/load-more/) — a list that offers the rest of the rows
- [Acting on data — a calm grid header, filters as chips, and a toolbar that follows the selection](https://osysharp.com/reference/ui/selection/) — `SearchField` and the grid's own filters
