Osy#betaRun it on Osyrin: osyrin.com

Reference / UI

A picker over every row — the SearchPicker

SearchPicker("Vendor", value: vendor, search: find, options: matches) { v => Text(v.Name); }

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.

stable1 example compiled by CIuilintquerylimits

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.

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#

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#

How it behaves#

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?#

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) the rows after it are not offered at all — the one being looked for is simply missing.

When is it 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?#

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#

Related

How many rows a page is sent

A page's query is sent bounded. Its own .Take(n) is the ceiling: the browser may ask for fewer of those rows, never…

Lists that load more

A page's list over a big table holds the first answer's rows (about 2 MB of them). rows.HasMore says whether the set…

Acting on data — a calm grid header, filters as chips, and a toolbar that follows the selection

A DataGrid reads calmly: its per-column filter row is shut until somebody asks to filter (the Filter toggle, or a…

A list or table with no bound

osy lint's ui-unbounded-list-does-not-page (SHOULD) fires when a page draws a list with foreach from a query that reads…