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

Source: https://ngwr.dev/reference/components/input-number  
Kind: Signal Forms

## Installation

```angular-ts
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`.

```angular-html
<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`.

```angular-html
<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](https://ngwr.dev/guides/forms) 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.

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

## Suffix and fractional step

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

## Without steppers

Hide the ▲▼ column for a plain numeric input.

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

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `min` | Minimum allowed value. | `number \| undefined` | `-Infinity` |
| `max` | Maximum allowed value. | `number \| undefined` | `Infinity` |
| `step` | Step used by stepper buttons + arrow keys. | `number` | `1` |
| `decimals` | Fixed 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 \| null` | `null` |
| `showSteppers` | Render the ▲▼ stepper column. | `boolean` | `true` |
| `size` | Control 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'` |
| `rounded` | Pill-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 \| null` | `false` |
| `prefix` | Optional prefix label (e.g. `"$"`). | `string` | `''` |
| `suffix` | Optional suffix label (e.g. `"kg"`). | `string` | `''` |
| `placeholder` | Placeholder shown when the input is empty. | `string` | `''` |
| `ariaLabel` | Accessible 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 \| null` | `null` |
| `incrementLabel` | Increment button aria-label. Falls back to `inputNumber.increment`. | `string \| null` | `null` |
| `decrementLabel` | Decrement button aria-label. Falls back to `inputNumber.decrement`. | `string \| null` | `null` |
| `disabled` | Disable interaction. | `boolean` | `false` |
| `readonly` | Read-only — values cannot be changed (steppers + typing disabled). | `boolean` | `false` |
| `value` | Numeric value (`null` when empty). Bound by `[formField]`, or two-way via `[(value)]`. | `number \| null` | `null` |
| `(touch)` | Emitted on blur so a bound field can mark itself touched. | `void` | — |
