@isa/utils/positive-integer-input
An Angular directive that ensures only positive integers can be entered into number input fields through keyboard blocking, paste sanitization, and input validation.
Overview
The PositiveIntegerInputDirective provides three layers of protection to prevent invalid input in number fields:
- Keyboard input blocking - Prevents invalid characters from being typed
- Paste sanitization - Cleans pasted content to extract only positive integers
- General input validation - Catches any other input methods (drag & drop, autofill, voice input, etc.)
This directive is particularly useful for scenarios where you need to enforce strictly positive integer values, such as:
- Quantity inputs in shopping carts
- Age or count fields
- Point or score inputs
- Any numeric field that should not accept decimals, negatives, or special characters
Key Behaviors
Blocked characters: ., ,, -, +, e, E, and all other non-digit characters
Important notes:
- This directive removes ALL non-digit characters, so
1.58becomes158, not1or2 - Leading zeros are automatically removed (
007becomes7) - A single
0input results in an empty field - Works seamlessly with Angular forms (
ngModel,formControl,formControlName)
Installation
Import the directive in your component:
import { PositiveIntegerInputDirective } from '@isa/utils/positive-integer-input';
@Component({
selector: 'app-my-component',
standalone: true,
imports: [PositiveIntegerInputDirective],
// ...
})
export class MyComponent {}
API Reference
PositiveIntegerInputDirective
Selector: input[type="number"][positiveIntegerInput]
Standalone: Yes
A standalone Angular directive that attaches to input[type="number"] elements and enforces positive integer input.
Host Listeners
| Event | Handler | Description |
|---|---|---|
keydown |
onKeyDown(event) |
Blocks invalid characters (., ,, -, +, e, E) before they can be entered |
paste |
onPaste(event) |
Intercepts paste events, sanitizes clipboard content, and inserts only positive integers |
input |
onInput(event) |
Catch-all for other input methods (drag & drop, autofill, voice input, IME) |
Private Methods
sanitizeInput(value: string): string
Sanitizes a string to contain only positive integers by:
- Removing all non-digit characters using regex
\D - Removing leading zeros to prevent parsing issues
Usage Examples
Basic Usage
<input type="number" positiveIntegerInput />
With ngModel (Two-way Binding)
<input
type="number"
positiveIntegerInput
[(ngModel)]="quantity"
placeholder="Enter quantity"
/>
export class MyComponent {
quantity: number | undefined;
}
With Reactive Forms (FormControl)
<input
type="number"
positiveIntegerInput
[formControl]="pointsControl"
placeholder="Enter points"
/>
import { FormControl } from '@angular/forms';
export class MyComponent {
pointsControl = new FormControl<number | null>(null);
}
With Reactive Forms (FormGroup)
<form [formGroup]="orderForm">
<input
type="number"
positiveIntegerInput
formControlName="quantity"
placeholder="Enter quantity"
/>
</form>
import { FormBuilder, FormGroup } from '@angular/forms';
export class MyComponent {
orderForm: FormGroup;
constructor(private fb: FormBuilder) {
this.orderForm = this.fb.group({
quantity: [null]
});
}
}
Complete Example with Validation
<form [formGroup]="productForm">
<label for="quantity">Quantity:</label>
<input
id="quantity"
type="number"
positiveIntegerInput
formControlName="quantity"
placeholder="Enter quantity"
[class.error]="quantity.invalid && quantity.touched"
/>
@if (quantity.invalid && quantity.touched) {
<span class="error-message">
Quantity is required and must be a positive integer
</span>
}
</form>
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { PositiveIntegerInputDirective } from '@isa/utils/positive-integer-input';
@Component({
selector: 'app-product-form',
standalone: true,
imports: [ReactiveFormsModule, PositiveIntegerInputDirective],
templateUrl: './product-form.component.html',
})
export class ProductFormComponent {
productForm: FormGroup;
constructor(private fb: FormBuilder) {
this.productForm = this.fb.group({
quantity: [null, [Validators.required, Validators.min(1)]]
});
}
get quantity() {
return this.productForm.get('quantity')!;
}
onSubmit() {
if (this.productForm.valid) {
console.log('Quantity:', this.quantity.value);
}
}
}
Behavior Examples
Keyboard Input
| User Action | Result | Reason |
|---|---|---|
Types 123 |
123 appears |
Valid digits allowed |
Types 1-2-3 |
Only 123 appears |
Minus signs blocked |
Types 1.5 |
Only 15 appears |
Decimal point blocked |
Types 1,000 |
Only 1000 appears |
Comma blocked |
Types +42 |
Only 42 appears |
Plus sign blocked |
Types 1e5 |
Only 15 appears |
Letter e blocked |
Paste Operations
| Clipboard Content | Result | Reason |
|---|---|---|
-100 |
100 |
Negative sign removed |
1.000 |
1000 |
Decimal point removed |
1,58 |
158 |
Comma removed (NOT rounded to 1 or 2) |
abc123xyz |
123 |
Non-digit characters removed |
007 |
7 |
Leading zeros removed |
+42 |
42 |
Plus sign removed |
3.14e2 |
3142 |
All non-digits removed |
0 |
Empty field | Single zero becomes empty string |
Other Input Methods
The directive also sanitizes input from:
- Drag and drop - Dropping text from other sources
- Browser autofill - Autocomplete values
- Voice input - Speech-to-text input
- IME input - Input Method Editors (Asian languages)
- Right-click paste - Context menu paste (some browsers)
Browser Compatibility
Works with all modern browsers that support Angular 20+ and native input[type="number"] elements.
Testing
The directive includes comprehensive test coverage with Vitest, including:
- Keyboard input blocking tests
- Paste sanitization tests
- General input validation tests
- Angular forms integration tests
- Edge case handling
Run tests:
nx test utils-positive-integer-input