Signal Forms

Number Input

Numeric input with locale-aware formatting and ▲▼ stepper buttons. Value is number | null. Parses leniently while typing, re-formats with grouping + fixed decimals on blur.

Installation

import { WrInputNumber } from 'ngwr/input-number';

@Component({ imports: [WrInputNumber] })
export class MyComponent {
  protected readonly value = signal<number | null>(0);
}

Basic

Locale comes from Angular's LOCALE_ID.

<wr-input-number [(value)]="value" />

Min / max / step

[min] and [max] bound what this control's own edits produce — typing, the ▲▼ buttons, the arrow keys. Arrow keys bump by step, Shift+Arrow by 10 × step.

<wr-input-number [(value)]="value" [min]="0" [max]="100" [step]="5" />

They do not clamp a value written in from outside. Set 500 on a field bound with [max]="100" and the field shows 500 and the model keeps 500; only the next edit is pulled back into range. That asymmetry is deliberate, and the same rule wr-slider and wr-rating follow: writing the clamp back would erase the out-of-range value a Validators.max exists to report, and would mark a pristine form dirty on first paint. Under [formField] it is not even possible to do better — binding [min] beside [formField] is a compile error, so the bounds arrive only through the schema's own min() / max() rules. See the reactive-forms guide for the whole table.

Two more things about what the field holds. While it has focus, typing owns the text — a typed 500 is committed as 100 immediately and the text is only re-formatted from the model on blur. And an emptied field is null, never 0; text that does not parse yet (a lone -, a trailing .) leaves the committed number alone.

Prefix and decimals

Fix the number of decimals shown on blur.

<wr-input-number [(value)]="price" prefix="$" [decimals]="2" />
$

Suffix and fractional step

<wr-input-number [(value)]="weight" suffix="kg" [decimals]="1" [step]="0.1" />
kg

Without steppers

Hide the ▲▼ column for a plain numeric input.

<wr-input-number [(value)]="value" [showSteppers]="false" />

API

NameDescriptionTypeDefault
minMinimum allowed value.number | undefined-Infinity
maxMaximum allowed value.number | undefinedInfinity
stepStep used by stepper buttons + arrow keys.number1
decimalsFixed number of decimals shown on blur. null keeps the entered precision. Clamped to what toFixed accepts — 0 to 100 — because it is the one numeric input on this component that had no transform, and the value goes straight into toFixed, which THROWS a RangeError outside that range rather than degrading. A [decimals]="-1" bound from a config object took the whole component down on blur.number | nullnull
showSteppersRender the ▲▼ stepper column.booleantrue
sizeControl size — forwarded to the field, and shares the --wr-control-* contract. Unset falls back to the inputNumber.size app default from provideWrConfig(), then to the input.size one, then to md.WrInputSize | null'md'
roundedPill-shaped corners. Unset falls back to the inputNumber.rounded app default from provideWrConfig(), then to the input.rounded one; [rounded]="false" turns a configured true back off.boolean | nullfalse
prefixOptional prefix label (e.g. "$").string''
suffixOptional suffix label (e.g. "kg").string''
placeholderPlaceholder shown when the input is empty.string''
ariaLabelAccessible name for the field. Unset, the field is named by whatever labels it — there is deliberately no fallback here. It used to mirror the placeholder, and an aria-label OUTRANKS a <label> in the accname order: inside a <wr-form-field label="Quantity"> the very input that adopts the field's id announced the placeholder ("0") instead, and a placeholder disappears as soon as the user types. Standalone nothing was lost by dropping it — an input with no other name already falls back to its own placeholder attribute, which is the name axe's label rule accepts too.string | nullnull
incrementLabelIncrement button aria-label. Falls back to inputNumber.increment.string | nullnull
decrementLabelDecrement button aria-label. Falls back to inputNumber.decrement.string | nullnull
disabledDisable interaction.booleanfalse
readonlyRead-only — values cannot be changed (steppers + typing disabled).booleanfalse
valueNumeric value (null when empty). Bound by [formField], or two-way via [(value)].number | nullnull
(touch)Emitted on blur so a bound field can mark itself touched.void