Pattern: value help
Who uses it: anyone typing a code they cannot remember — a buyer picking a material out of forty thousand, a clerk choosing a cost centre; dozens of times a day, inside a form they are part-way through. What "done" looks like: the right code is in the field, and the reader is back where they were with nothing else lost. The picker is a detour, not a destination — which is why choosing must put focus back in the field they were filling, not leave it at the top of the document. Not when the option set is small — a couple dozen fixed choices belong in a plain native select, not a dialog with search and paging behind it. This pattern is for the case a select cannot handle: a list too large to enumerate in one control.
Also called: search help or
F4 help in SAP, a value list, a lookup, a
picker. Same screen; this page is it (roadmap 132.2).
Measured against what an F4 help characteristically offers:
multi-criteria search is here already — the
filter bar above the results
takes as many criteria as the field needs, and the demo runs two;
a configurable hit list needs nothing new either, because
initTableToolbar() already ships column visibility and the
results are an ordinary data
table; a personal value list — the reader's recent or
favourite picks — is a group of rows above the results, and the list itself
is your data, not the framework's. None of the three is a second pattern.
The screen
Open it, type seal, and pick a row:
the code lands in the field, the dialog closes, and focus returns to the
control you left. Every part of this is a component that already ships —
this pattern adds no CSS and no new component.
Anatomy
- The field and its trigger — a plain
input beside a button carrying
data-dialog-trigger. The field stays editable: someone who knows the code types it and never opens the picker. - The picker — a dialog, modal because choosing is the only thing to do while it is open.
- Search & filters — filter bar at the top of the body, so narrowing happens where the eye already is.
- Results — a data table; the first cell is a button, not a link, because picking changes this form rather than navigating away.
- The filtered empty — state, and it says your filter excluded everything, which is a different message from "there is no master data".
- Result count and paging — pagination in the footer, where it does not compete with the results.
Data contract
The picker is a search endpoint, not a copy of the table. Forty thousand materials never reach the browser.
GET /materials?q=&group=&page=→ one page of matches plus a total. The total drives both the count and whether paging appears at all.- Debounce the search server-side of the keystroke — one request per pause, not per character. The demo above filters in the browser only because six rows fit in the page.
- Picking returns the whole record, not just the code: the form usually needs the description and unit too, and a second request to fetch what the picker already had is a wasted round trip.
- A 4xx leaves the dialog open with the error in place of the results. Closing it would discard the reader's search.
States
| State | What the screen shows |
|---|---|
| Loading | The table keeps its header and shows data-loading; the filter bar stays usable so a slow query can be narrowed rather than waited out. |
| Empty — no data at all | "No materials exist yet", with the action that creates one. This is a setup problem. |
| Empty — filtered out | A different message: the data exists, the filter excluded it, and the action is clear filters. Showing the setup message here sends the reader to create a duplicate. |
| Too many results | Page, and say the total. "Showing 1–20 of 4,318" tells the reader to narrow; a bare first page does not. |
| Error | Inside the dialog, results replaced, search preserved. |
| Permission | If the reader may not browse master data, the trigger is absent — not disabled. A disabled button invites a support ticket. |
| No JS | Like the command bar, the "Find…" trigger has no native popovertarget — initDialogs()'s click listener is the only thing that calls showModal(). With no JS the trigger is inert: the picker dialog never opens, so the field can only be filled by typing a value directly, and the in-dialog filtering script never runs. |
Markup
<!-- the field, and the control that opens the picker -->
<div class="bo-form-field">
<label class="bo-form-field__label" for="vh-material">Material</label>
<div class="bo-cluster">
<input class="bo-input bo-input--code" id="vh-material" value="MAT-4471" />
<button class="bo-btn bo-btn--secondary" type="button"
data-dialog-trigger="vh-dialog" aria-label="Find a material">Find…</button>
</div>
</div>
<dialog class="bo-dialog bo-dialog--wide" id="vh-dialog" aria-labelledby="vh-title">
<div class="bo-dialog__header">
<h2 class="bo-dialog__title" id="vh-title">Find a material</h2>
<form method="dialog"><button class="bo-btn bo-btn--sm bo-btn--ghost" type="submit">×</button></form>
</div>
<div class="bo-dialog__body">
<form class="bo-filter-bar" role="search">…search + filters…</form>
<table class="bo-data-table">…results…</table>
<div class="bo-state" hidden>…the empty that means "your filter excluded everything"…</div>
</div>
<div class="bo-dialog__footer">
<nav class="bo-pagination">…</nav>
</div>
</dialog> Components used
complexity 2 of 4