forty-cdk
llms.txt

Primitives

Search

A role='searchbox' text input that mirrors its value to a signal and reflects validation state, paired with a clear button that self-hides while the field is empty. Reuses forInput's form-value wiring, so it auto-wires with Signal Forms and Field.

forty-cdk/search

Type in the box and clear it again. The clear button is yours to render, and the host reflects data-empty for as long as there is nothing to clear.

  • Accordion
  • Breadcrumbs
  • Combobox
  • Date Picker
  • Dialog
  • File Upload
  • Listbox
  • Pagination
  • Popover
  • Slider
  • Switch
  • Tooltip

[forSearch] applies on a native <input>, wires its value to a signal, reflects validation state, and exposes a clear() / focusInput() API for the companion [forSearchClear] clear button.

Anatomy

<div forSearchGroup>
  <input forSearch [(value)]="query" placeholder="Search…" />
  <button forSearchClear ariaLabel="Clear search">×</button>
</div>

[forSearchClear] self-hides while the value is empty and refocuses the input on activation. Wrap the field and the button in a [forSearchGroup] so the button can coordinate with the field. The void <input> can't contain the button as a DOM descendant, so they bridge through the group registry. A standalone [forSearch] (no clear button) needs no group.

Examples

Inside a Field with Signal Forms

forSearch implements FormValueControl<string>, so [formField] auto-wires it inside forField exactly like forInput: the label adopts the control id, validation flows into aria-errormessage, and aria-invalid / aria-required are reflected. Type one or two characters and blur to surface the error.

With Signal Forms and Field

<div forField>
  <label forLabel>Search</label>
  <input forSearch [formField]="searchForm.query" />
</div>

[formField] auto-wires the FormValueControl<string> contract: required, invalid, touched, and the value itself flow in and out without extra glue.

Command palette

@if (paletteOpen()) {
<div forDialog ariaLabel="Command palette" (dismiss)="paletteOpen.set(false)">
  <input forSearch [(value)]="query" [clearOnEscape]="false" placeholder="Type a command…" />
  <!-- results… -->
</div>
}

When the search box is the overlay's only content, [clearOnEscape]="false" makes the first Escape dismiss the palette instead of clearing the query first (see Keyboard below).

API

ForSearch

Applied to a native <input>. Sets role="searchbox", mirrors the value to a signal, reflects validation state, and exposes clear() / focusInput() for the companion clear button. Implements FormValueControl<string>, so it auto-wires with [formField] and auto-associates inside a [forField].

clear() is a no-op while the field is disabled or read-only, whichever caller invokes it: the clear button, the Escape key, or your own code through the [forSearchGroup] context.

InputTypeDefaultDescription
clearOnEscape
boolean
trueWhether Escape clears a non-empty value before propagating. Set to false for command-palette compositions.
Data attributeValues
data-disabledpresent when the field is disabled
data-readonlypresent when the field is read-only
data-emptypresent while the value is '' (empty)

ForSearchGroup

Optional coordination wrapper. Renders nothing and imposes no role or layout; it bridges the [forSearchClear] button to the [forSearch] field through a registry, since the void <input> can't contain the button as a descendant. Required only when you use the clear button.

ForSearchClear

Clear button. Apply on a <button> inside a [forSearchGroup] that also wraps the [forSearch]. No instance is passed through the template. Self-hides while the value is empty and refocuses the input on activation.

InputTypeDefaultDescription
ariaLabel
string | null
scope clearAriaLabel ('Clear')Accessible name for the icon-only button. Set to null to drop the attribute.

Keyboard

KeyBehaviour
EscapeClears a non-empty value, matching the native <input type="search"> affordance.

Escape is consumed (preventDefault() + stopPropagation()) only when it clears. When the field is already empty (or disabled / read-only, where clearing is a no-op), the key is left to propagate, so a [forSearch] placed inside a Dialog, Popover, or Combobox panel does not swallow that overlay's own Escape dismissal. A non-empty search box inside an overlay therefore takes two presses: the first clears the field, the second closes the overlay.

That is the right default for a searchbox alongside other content, and the wrong one for a command palette, where the search box is the dialog's only content and one Escape should close it. Opt out with [clearOnEscape]="false": the directive then neither acts on nor consumes Escape, so the enclosing dismissible layer sees it on the first press even with a non-empty query.

<input forSearch [(value)]="query" [clearOnEscape]="false" />

The propagation rule for an empty, disabled, or read-only field is unchanged: Escape passes through untouched in all three cases regardless of clearOnEscape.

Accessibility

  • The role="searchbox" attribute is set statically by the directive.

  • Validation state (aria-required, aria-invalid, aria-readonly) is reflected as truthy-only attributes (absent when false). The disabled state reflects through the native disabled attribute alone (no aria-disabled), so style it with :disabled or [data-disabled].

  • [forSearchClear] carries aria-label="Clear" by default so the icon-only button has an accessible name. Override it per-instance with [ariaLabel], or centrally (and for localization) with provideForSearchDefaults:

    providers: [provideForSearchDefaults({ clearAriaLabel: 'Limpiar' })];

    Set [ariaLabel]="null" to drop the attribute entirely.