# @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:
1. **Keyboard input blocking** - Prevents invalid characters from being typed
2. **Paste sanitization** - Cleans pasted content to extract only positive integers
3. **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.58` becomes `158`, not `1` or `2`
- Leading zeros are automatically removed (`007` becomes `7`)
- A single `0` input results in an empty field
- Works seamlessly with Angular forms (`ngModel`, `formControl`, `formControlName`)
## Installation
Import the directive in your component:
```ts
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:
1. Removing all non-digit characters using regex `\D`
2. Removing leading zeros to prevent parsing issues
## Usage Examples
### Basic Usage
```html
```
### With ngModel (Two-way Binding)
```html
```
```ts
export class MyComponent {
quantity: number | undefined;
}
```
### With Reactive Forms (FormControl)
```html
```
```ts
import { FormControl } from '@angular/forms';
export class MyComponent {
pointsControl = new FormControl(null);
}
```
### With Reactive Forms (FormGroup)
```html
```
```ts
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
```html
```
```ts
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:
```bash
nx test utils-positive-integer-input
```
## Related
- [Angular Forms Documentation](https://angular.dev/guide/forms)
- [Angular Directives Documentation](https://angular.dev/guide/directives)
- [HTML Input Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)