Number Input

The number input component provides a structured way for users to input numerical data with built-in validation and controls. It combines a text input field with stepper buttons and can include unit selection functionality for measurements, currency, or other quantifiable values.

import {NumberInputModule} from "@qualcomm-ui/angular/number-input"

Overview

  • This component implements the ARIA Spinbutton pattern. The Home and End keys work differently than a text input:
    • Home: If the spinbutton has a minimum value, sets the value to its minimum.
    • End: If the spinbutton has a maximum value, sets the value to its maximum.
  • Template-driven and Reactive form values are supplied as numbers. When the field is cleared, the value is set to NaN. You must account for this in your application logic.

Examples

Simple

The simple API bundles all subcomponents together into a single directive.

<q-number-input class="w-72" label="Label" placeholder="Enter a number" />

Child Directives

Provide child directives to customize specific elements while keeping the simple API's default structure.

<q-number-input class="w-72" placeholder="Enter a number">
  <label q-number-input-label>Label</label>
</q-number-input>

Composite

Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.

<div class="w-72" q-number-input-root>
  <label q-number-input-label>Label</label>
  <div q-number-input-input-group>
    <input placeholder="Enter a number" q-number-input-input />
    <div q-number-input-control></div>
    <span q-number-input-error-indicator></span>
  </div>
  <div q-number-input-error-text>Error</div>
</div>

Min and Max values

Pass the min and max props to the root component to set the minimum and maximum values of the number input.

If the value entered is less than min or greater than max, it will be clamped to the nearest boundary on blur or enter.

<q-number-input class="w-72" defaultValue="7" max="10" min="5" />

Step interval

Pass the step prop to the root component to set the increment or decrement step interval.

<q-number-input class="w-72" placeholder="Enter a number" step="3" />

States

The following shows how the component appears in each interactive state.

Invalid
<q-number-input disabled label="Disabled" placeholder="Disabled" />
<q-number-input label="Read only" placeholder="Read only" readOnly />
<q-number-input
  errorText="Invalid"
  invalid
  label="Invalid"
  placeholder="Invalid"
/>

Sizes

Customize size using the size prop. The default size is md.

<q-number-input
  class="w-56"
  placeholder="sm"
  size="sm"
  startIcon="Sigma"
/>
<q-number-input
  class="w-64"
  placeholder="md"
  size="md"
  startIcon="Sigma"
/>
<q-number-input
  class="w-72"
  placeholder="lg"
  size="lg"
  startIcon="Sigma"
/>

Unit Selector

Add a unit selector to the number input by passing an array of options to the unitOptions prop.

The selected unit follows our controlled state pattern: use defaultUnit for uncontrolled state, or unit with (unitChanged) for controlled state.

<q-number-input
  class="w-72"
  defaultUnit="USD"
  label="Price"
  placeholder="0.00"
  [unitOptions]="unitOptions"
/>

Error Text and Indicator

Error messages are displayed using two props:

  • invalid
  • errorText (or the q-number-input-error-text directive when using the composite API)

The error text and indicator will only render when invalid is true.

Value must be greater than 0
import {Component, computed, signal} from "@angular/core"
import {FormsModule} from "@angular/forms"

import {NumberInputModule} from "@qualcomm-ui/angular/number-input"

@Component({
  imports: [NumberInputModule, FormsModule],
  selector: "number-input-error-text-demo",
  template: `
    <q-number-input
      class="w-72"
      defaultValue="0"
      errorText="Value must be greater than 0"
      label="Label"
      placeholder="Enter a value"
      [invalid]="isInvalid()"
    />
  `,
})
export class NumberInputErrorTextDemo {
  readonly value = signal<number>(0)

  readonly isInvalid = computed(() => {
    const value = this.value()
    return isNaN(value) || value <= 0
  })
}

Forms

Template Forms

When using template-driven forms with ngModel, perform validation manually in your component and pass the result to the invalid property.

TIP

Form validation timing is controlled by the updateOn property on the form control.

<q-number-input
  class="w-72"
  errorText="Value must be greater than 0"
  label="Label"
  placeholder="Enter a value"
  [invalid]="isInvalid()"
  [(ngModel)]="value"
/>

Required

When using template forms, pass the required property to apply a validator to the form control.

<q-number-input
  #textInput
  class="w-72"
  label="Required"
  placeholder="Enter a value"
  required
  [(ngModel)]="value"
/>

Reactive Forms

Use Reactive forms for better control of form state and validation.

import {Component, inject} from "@angular/core"
import {
  type AbstractControl,
  FormBuilder,
  ReactiveFormsModule,
  type ValidationErrors,
  Validators,
} from "@angular/forms"

import {ErrorTextComponent} from "@qualcomm-ui/angular/input"
import {NumberInputModule} from "@qualcomm-ui/angular/number-input"
import {requiredNumberValidator} from "@qualcomm-ui/angular-core/number-input"

function aspectRatioValidator(
  control: AbstractControl,
): ValidationErrors | null {
  const width = control.get("width")?.value
  const height = control.get("height")?.value
  const ratio = width / height
  return ratio >= 0.25 && ratio <= 4 ? null : {invalidAspectRatio: true}
}

@Component({
  imports: [NumberInputModule, ReactiveFormsModule, ErrorTextComponent],
  selector: "number-input-reactive-forms-demo",
  template: `
    <form class="flex flex-col gap-3" [formGroup]="aspectRatioFormGroup">
      <div class="flex flex-wrap items-start gap-2">
        <q-number-input
          class="w-56"
          errorText="Width must be between 1 and 4096"
          formControlName="width"
          label="Width"
          step="0.01"
        />
        <q-number-input
          class="w-56"
          errorText="Height must be between 1 and 4096"
          formControlName="height"
          label="Height"
          step="0.01"
        />
      </div>
      @if (aspectRatioFormGroup.hasError("invalidAspectRatio")) {
        <div q-error-text>
          Invalid aspect ratio: {{ aspectRatio() }} (must be between 0.25 and 4)
        </div>
      }
    </form>
  `,
})
export class NumberInputReactiveFormsDemo {
  protected readonly fb = inject(FormBuilder)

  aspectRatioFormGroup = this.fb.group(
    {
      height: [
        600,
        [Validators.min(1), Validators.max(4096), requiredNumberValidator],
      ],
      width: [
        800,
        [Validators.min(1), Validators.max(4096), requiredNumberValidator],
      ],
    },
    {validators: [Validators.required, aspectRatioValidator]},
  )

  aspectRatio() {
    const height = this.aspectRatioFormGroup.get("height")?.value || 1
    const width = this.aspectRatioFormGroup.get("width")?.value || 0
    return (width / height).toFixed(5)
  }
}

State Guidelines

The disabled, invalid, and required properties have no effect when using Reactive Forms. Use the equivalent Reactive Form bindings instead:

disabledField = new FormControl(5)
invalidField = new FormControl(0, {
  validators: [Validators.min(1)],
})
requiredField = new FormControl(5, {validators: [requiredNumberValidator]})

ngOnInit() {
  this.disabledField.disable()
  this.invalidField.markAsDirty()
}

Composite API & Forms

When using the composite API with Reactive Forms, always apply form control bindings to the q-number-input-root directive.

import {Component, computed, signal} from "@angular/core"
import {FormsModule} from "@angular/forms"

import {NumberInputModule} from "@qualcomm-ui/angular/number-input"

@Component({
  imports: [NumberInputModule, FormsModule],
  selector: "number-input-composite-forms-demo",
  template: `
    <div
      class="w-72"
      q-number-input-root
      [invalid]="isInvalid()"
      [(ngModel)]="value"
    >
      <label q-number-input-label>Composite Forms</label>
      <div q-number-input-input-group>
        <input placeholder="Enter a value" q-number-input-input />
        <div q-number-input-control></div>
        <span q-number-input-error-indicator></span>
      </div>
      <div q-number-input-error-text>Value must be greater than 0</div>
    </div>
  `,
})
export class NumberInputCompositeFormsDemo {
  readonly value = signal<number>(0)

  readonly isInvalid = computed(() => {
    return isNaN(this.value()) || this.value() <= 0
  })
}

API

<q-number-input>

The <q-number-input> component extends the q-number-input-root directive with the following properties:

PropType
Optional error that describes the element when invalid is true.
string
Optional hint describing the element. This element is automatically associated with the component's input element for accessibility.
string
Optional label describing the element. Recommended. This element is automatically associated with the component's input element for accessibility.
string
HTML placeholder attribute, passed to the internal input element.
string
Type
string
Description
Optional error that describes the element when invalid is true.
Type
string
Description
Optional hint describing the element. This element is automatically associated with the component's input element for accessibility.
Type
string
Description
Optional label describing the element. Recommended. This element is automatically associated with the component's input element for accessibility.
Type
string
Description
HTML placeholder attribute, passed to the internal input element.

Composite API

q-number-input-root

PropTypeDefault
Whether to allow mouse wheel to change the value
boolean
Whether to allow the value to overflow the min/max range
boolean
true
Whether to clamp the value when the input loses focus (blur)
boolean
true
The initial unit when uncontrolled. Defaults to first option if not provided.
string
The initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox. This property will be ignored if you opt into controlled state via form control bindings.
number
The document's text/writing direction.
'ltr' | 'rtl'
"ltr"
Controls whether the input is disabled in template-driven forms. When true, prevents user interaction and applies visual styling to indicate the disabled state.
boolean
lucide-angular icon, positioned after the input.
| string
| LucideIconData
Whether to focus the input when the value changes
boolean
true
The options to pass to the Intl.NumberFormat constructor
NumberFormatOptions
A root node to correctly resolve the Document in custom environments. i.e., Iframes, Electron.
() =>
| Node
| ShadowRoot
| Document
Hints at the type of data that might be entered by the user. It also determines the type of keyboard shown to the user on mobile devices
| 'text'
| 'tel'
| 'numeric'
| 'decimal'
"decimal"
Controls the visual error state of the input. When true, applies semantic error styling to indicate validation failure.
boolean
The current locale. Based on the BCP 47 definition.
string
'en-US'
The maximum value of the number input
number
Number.MAX_SAFE_INTEGER
The minimum value of the number input
number
Number.MIN_SAFE_INTEGER
The name of the input field in a checkbox. Useful for form submission.
string
The pattern used to check the element's value against
string
"[0-9]*(.[0-9]+)?"
Whether the input is read-only. When true, prevents user interaction while keeping the input focusable and visible.
boolean
Controls whether the input is required in template-driven forms. When true, the input must have a value for form validation to pass.
boolean
The size of the input field and its elements. Governs properties like font size, item padding, and icon sizes.
| 'sm'
| 'md'
| 'lg'
'md'
Whether to spin the value when the increment/decrement button is pressed
boolean
true
lucide-angular icon, positioned before the input.
| string
| LucideIconData
Amount to increment/decrement the value when using stepper buttons or arrow keys.
number
1
Specifies the localized strings that identify the accessibility elements and their states
{
decrementLabel?: string
incrementLabel?: string
valueText?: (
value: string,
) => string
}
The controlled unit value.
string
Array of unit options to display in the unit selector.
Array<{
displayText?: string
label: string
value: string
}>
Event emitted when the selected unit changes.
string
Event emitted when the checkbox is checked or unchecked. This is only emitted on interaction. It doesn't emit in response to programmatic form control changes.
{
value: string
valueAsNumber: number
}
Function invoked when the value overflows or underflows the min/max range
{
reason:
| 'rangeUnderflow'
| 'rangeOverflow'
value: string
valueAsNumber: number
}
Type
boolean
Description
Whether to allow mouse wheel to change the value
Type
boolean
Description
Whether to allow the value to overflow the min/max range
Type
boolean
Description
Whether to clamp the value when the input loses focus (blur)
Type
string
Description
The initial unit when uncontrolled. Defaults to first option if not provided.
Type
number
Description
The initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox. This property will be ignored if you opt into controlled state via form control bindings.
Type
'ltr' | 'rtl'
Description
The document's text/writing direction.
Type
boolean
Description
Controls whether the input is disabled in template-driven forms. When true, prevents user interaction and applies visual styling to indicate the disabled state.
Type
| string
| LucideIconData
Description
lucide-angular icon, positioned after the input.
Type
boolean
Description
Whether to focus the input when the value changes
Type
NumberFormatOptions
Description
The options to pass to the Intl.NumberFormat constructor
Type
() =>
| Node
| ShadowRoot
| Document
Description
A root node to correctly resolve the Document in custom environments. i.e., Iframes, Electron.
Type
| 'text'
| 'tel'
| 'numeric'
| 'decimal'
Description
Hints at the type of data that might be entered by the user. It also determines the type of keyboard shown to the user on mobile devices
Type
boolean
Description
Controls the visual error state of the input. When true, applies semantic error styling to indicate validation failure.
Type
string
Description
The current locale. Based on the BCP 47 definition.
Type
number
Description
The maximum value of the number input
Type
number
Description
The minimum value of the number input
Type
string
Description
The name of the input field in a checkbox. Useful for form submission.
Type
string
Description
The pattern used to check the element's value against
Type
boolean
Description
Whether the input is read-only. When true, prevents user interaction while keeping the input focusable and visible.
Type
boolean
Description
Controls whether the input is required in template-driven forms. When true, the input must have a value for form validation to pass.
Type
| 'sm'
| 'md'
| 'lg'
Description
The size of the input field and its elements. Governs properties like font size, item padding, and icon sizes.
Type
boolean
Description
Whether to spin the value when the increment/decrement button is pressed
Type
| string
| LucideIconData
Description
lucide-angular icon, positioned before the input.
Type
number
Description
Amount to increment/decrement the value when using stepper buttons or arrow keys.
Type
{
decrementLabel?: string
incrementLabel?: string
valueText?: (
value: string,
) => string
}
Description
Specifies the localized strings that identify the accessibility elements and their states
Type
string
Description
The controlled unit value.
Type
Array<{
displayText?: string
label: string
value: string
}>
Description
Array of unit options to display in the unit selector.
Type
string
Description
Event emitted when the selected unit changes.
Type
{
value: string
valueAsNumber: number
}
Description
Event emitted when the checkbox is checked or unchecked. This is only emitted on interaction. It doesn't emit in response to programmatic form control changes.
Type
{
reason:
| 'rangeUnderflow'
| 'rangeOverflow'
value: string
valueAsNumber: number
}
Description
Function invoked when the value overflows or underflows the min/max range

q-number-input-input-group

Entity not found: NumberInputInputGroupComponent

q-number-input-label

Entity not found: NumberInputLabelComponent

q-number-input-hint

PropType
string
Type
string

q-number-input-control

q-number-input-decrement-trigger

Entity not found: NumberInputDecrementTriggerComponent

q-number-input-increment-trigger

Entity not found: NumberInputIncrementTriggerComponent

q-number-input-input

PropType
string
Type
string

q-number-input-error-text

PropType
string
Type
string

q-number-input-error-indicator

Entity not found: NumberInputErrorIndicatorComponent

q-number-input-unit-select

PropTypeDefault
The positioning point for the menu. Can be set by the context menu trigger or the button trigger.
{
x: number
y: number
}
Whether to close the menu when an option is selected
boolean
true
Whether the menu is composed with other composite widgets like a combobox or tabs
boolean
true
The initial highlighted value of the menu item when rendered. Use when you don't need to control the highlighted value of the menu item.
string
The initial open state of the menu when rendered. Use when you don't need to control the open state of the menu.
boolean
The document's text/writing direction.
'ltr' | 'rtl'
"ltr"
A root node to correctly resolve the Document in custom environments. i.e., Iframes, Electron.
() =>
| Node
| ShadowRoot
| Document
The controlled highlighted value of the menu item.
string
id attribute. If omitted, a unique identifier will be generated for accessibility.)
string
Whether to synchronize the present change immediately or defer it to the next frame.
boolean
false
When true, the component will not be rendered in the DOM until it becomes visible or active.
boolean
false
Whether to loop the keyboard navigation.
boolean
false
The controlled open state of the menu
boolean
The options used to dynamically position the menu
PositioningOptions
The controlled presence of the node.
boolean
Whether to allow the initial presence animation.
boolean
false
Whether the pressing printable characters should trigger typeahead navigation
boolean
true
When true, the component will be completely removed from the DOM when it becomes inactive or hidden, rather than just being hidden with CSS.
boolean
false
Function called when the escape key is pressed
KeyboardEvent
Function called when the animation ends in the closed state
void
Function called when the focus is moved outside the component
CustomEvent<{
event?: E
}>
Function called when the highlighted menu item changes.
string
Function called when an interaction happens outside the component
| CustomEvent<{event?: E}>
| CustomEvent<{event?: E}>
Function to navigate to the selected item if it's an anchor element
{
href: string
node: HTMLAnchorElement
value: string
}
Function called when the open state changes
boolean
Function called when the pointer is pressed down outside the component
CustomEvent<{
event?: E
}>
Function called when this layer is closed due to a parent layer being closed
CustomEvent<{
originalIndex: number
originalLayer: HTMLElement
targetIndex: number
targetLayer: HTMLElement
}>
Function called when a menu item is selected.
string
Type
{
x: number
y: number
}
Description
The positioning point for the menu. Can be set by the context menu trigger or the button trigger.
Type
boolean
Description
Whether to close the menu when an option is selected
Type
boolean
Description
Whether the menu is composed with other composite widgets like a combobox or tabs
Type
string
Description
The initial highlighted value of the menu item when rendered. Use when you don't need to control the highlighted value of the menu item.
Type
boolean
Description
The initial open state of the menu when rendered. Use when you don't need to control the open state of the menu.
Type
'ltr' | 'rtl'
Description
The document's text/writing direction.
Type
() =>
| Node
| ShadowRoot
| Document
Description
A root node to correctly resolve the Document in custom environments. i.e., Iframes, Electron.
Type
string
Description
The controlled highlighted value of the menu item.
Type
string
Description
id attribute. If omitted, a unique identifier will be generated for accessibility.)
Type
boolean
Description
Whether to synchronize the present change immediately or defer it to the next frame.
Type
boolean
Description
When true, the component will not be rendered in the DOM until it becomes visible or active.
Type
boolean
Description
Whether to loop the keyboard navigation.
Type
boolean
Description
The controlled open state of the menu
Type
PositioningOptions
Description
The options used to dynamically position the menu
Type
boolean
Description
The controlled presence of the node.
Type
boolean
Description
Whether to allow the initial presence animation.
Type
boolean
Description
Whether the pressing printable characters should trigger typeahead navigation
Type
boolean
Description
When true, the component will be completely removed from the DOM when it becomes inactive or hidden, rather than just being hidden with CSS.
Type
KeyboardEvent
Description
Function called when the escape key is pressed
Type
void
Description
Function called when the animation ends in the closed state
Type
CustomEvent<{
event?: E
}>
Description
Function called when the focus is moved outside the component
Type
string
Description
Function called when the highlighted menu item changes.
Type
| CustomEvent<{event?: E}>
| CustomEvent<{event?: E}>
Description
Function called when an interaction happens outside the component
Type
{
href: string
node: HTMLAnchorElement
value: string
}
Description
Function to navigate to the selected item if it's an anchor element
Type
boolean
Description
Function called when the open state changes
Type
CustomEvent<{
event?: E
}>
Description
Function called when the pointer is pressed down outside the component
Type
CustomEvent<{
originalIndex: number
originalLayer: HTMLElement
targetIndex: number
targetLayer: HTMLElement
}>
Description
Function called when this layer is closed due to a parent layer being closed
Type
string
Description
Function called when a menu item is selected.

Data Structures

UnitOption

Each unit option object accepts the following properties:

PropType
Full text shown in dropdown menu. Defaults to label if not provided.
string
Short label shown on trigger button.
string
Internal value identifier.
string
Type
string
Description
Full text shown in dropdown menu. Defaults to label if not provided.
Type
string
Description
Short label shown on trigger button.
Type
string
Description
Internal value identifier.