# @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
@if (quantity.invalid && quantity.touched) { Quantity is required and must be a positive integer }
``` ```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)