ngx-st-qty-input
v18.0.8
Published
- [Overview](#overview) - [Installation](#installation) - [Basic Usage](#basic-usage) - [Inputs](#inputs) - [Outputs](#outputs) - [Usage Examples](#usage-examples) - [Best Practices](#best-practices)
Readme
Quantity Input Component - Complete Documentation
Table of Contents
Overview
The ngx-st-qty-input component is a quantity selector with increment/decrement buttons. Features include:
- Plus/minus buttons for incrementing/decrementing values
- Direct numeric input (spinner arrows hidden for cleaner UI)
- Negative value support (optional)
- Two-way data binding with model
- Disabled state support
- Error state display
- Change event emission
- Customizable input width
- Material Design form field appearance options
Installation
npm install ngx-st-qty-inputImport the module:
import { NgxStQtyInputModule } from 'ngx-st-qty-input';
@NgModule({
imports: [NgxStQtyInputModule]
})
export class AppModule { }Basic Usage
<ngx-st-qty-input
[(qtyModel)]="quantity">
</ngx-st-qty-input>
<p>Quantity: {{ quantity }}</p>Inputs
qtyModel (model signal)
- Type:
number - Required: Yes
- Description: The quantity value. Uses Angular's model signal for two-way binding.
- Example:
[(qtyModel)]="quantity"
allowNegative
- Type:
boolean - Default:
false - Description: Allows negative values. When false, values below 0 are automatically reset to 0.
- Example:
[allowNegative]="true" [allowNegative]="false"
disabled
- Type:
boolean - Default:
false - Description: Disables the entire component (input and buttons).
- Example:
[disabled]="isProcessing" [disabled]="true"
showError
- Type:
boolean - Default:
false - Description: Shows an error state styling on the input.
- Example:
[showError]="quantity < minRequired" [showError]="hasValidationError"
appearance
- Type:
'fill' | 'outline' - Default:
'fill' - Description: Material Design form field appearance. 'fill' for filled style, 'outline' for outlined style.
- Example:
[appearance]="'fill'" [appearance]="'outline'"
inputWidth
- Type:
string - Default:
'55px' - Description: CSS width value for the input field. Allows customization of the input width by parent components.
- Example:
[inputWidth]="'60px'" [inputWidth]="'80px'" [inputWidth]="'100%'"
Outputs
newValueEmitter
- Type:
number - Description: Emitted whenever the quantity value changes (via buttons or direct input).
- Example:
(newValueEmitter)="onQuantityChange($event)" onQuantityChange(newValue: number): void { console.log('New quantity:', newValue); this.updateCart(newValue); }
Usage Examples
Example 1: Basic Shopping Cart Quantity
// Component
@Component({
selector: 'app-cart-item',
template: `
<div class="cart-item">
<img [src]="product.image" [alt]="product.name">
<h3>{{ product.name }}</h3>
<p>Price: ${{ product.price }}</p>
<ngx-st-qty-input
[(qtyModel)]="quantity"
(newValueEmitter)="updateCartItem($event)">
</ngx-st-qty-input>
<p>Total: ${{ total }}</p>
</div>
`
})
export class CartItemComponent {
product = {
id: 1,
name: 'Product Name',
price: 19.99,
image: 'product.jpg'
};
quantity = 1;
get total(): number {
return this.product.price * this.quantity;
}
updateCartItem(newQuantity: number): void {
console.log('Updating cart with quantity:', newQuantity);
// API call to update cart
}
}Example 2: With Negative Values
// Component
@Component({
selector: 'app-adjustment',
template: `
<h3>Stock Adjustment</h3>
<p>Current Stock: {{ currentStock }}</p>
<label>Adjustment:</label>
<ngx-st-qty-input
[(qtyModel)]="adjustment"
[allowNegative]="true"
(newValueEmitter)="calculateNewStock($event)">
</ngx-st-qty-input>
<p>New Stock: {{ newStock }}</p>
<button (click)="applyAdjustment()">Apply</button>
`
})
export class StockAdjustmentComponent {
currentStock = 100;
adjustment = 0;
newStock = 100;
calculateNewStock(adjustment: number): void {
this.newStock = this.currentStock + adjustment;
}
applyAdjustment(): void {
this.currentStock = this.newStock;
this.adjustment = 0;
}
}Example 3: Disabled State
// Component
@Component({
selector: 'app-disabled-qty',
template: `
<ngx-st-qty-input
[(qtyModel)]="quantity"
[disabled]="outOfStock">
</ngx-st-qty-input>
<p *ngIf="outOfStock" class="error">Out of Stock</p>
<button (click)="toggleStock()">Toggle Stock</button>
`
})
export class DisabledQtyComponent {
quantity = 1;
outOfStock = false;
toggleStock(): void {
this.outOfStock = !this.outOfStock;
}
}Example 4: With Validation and Error Display
// Component
@Component({
selector: 'app-validated-qty',
template: `
<div class="qty-input-wrapper">
<label>Quantity (Min: {{ minQty }}, Max: {{ maxQty }}):</label>
<ngx-st-qty-input
[(qtyModel)]="quantity"
[showError]="hasError"
(newValueEmitter)="validate($event)">
</ngx-st-qty-input>
<p *ngIf="hasError" class="error-message">
{{ errorMessage }}
</p>
</div>
`
})
export class ValidatedQtyComponent {
quantity = 1;
minQty = 1;
maxQty = 10;
hasError = false;
errorMessage = '';
validate(value: number): void {
if (value < this.minQty) {
this.hasError = true;
this.errorMessage = `Minimum quantity is ${this.minQty}`;
} else if (value > this.maxQty) {
this.hasError = true;
this.errorMessage = `Maximum quantity is ${this.maxQty}`;
} else {
this.hasError = false;
this.errorMessage = '';
}
}
}Example 5: Multiple Quantity Inputs
// Component
@Component({
selector: 'app-order-form',
template: `
<h3>Order Products</h3>
<div *ngFor="let item of orderItems" class="order-item">
<span>{{ item.name }}</span>
<ngx-st-qty-input
[(qtyModel)]="item.quantity"
(newValueEmitter)="updateItemTotal(item)">
</ngx-st-qty-input>
<span>{{ item.price | currency }} each</span>
<span>Total: {{ item.total | currency }}</span>
</div>
<div class="order-summary">
<h4>Order Total: {{ orderTotal | currency }}</h4>
</div>
`
})
export class OrderFormComponent {
orderItems = [
{ id: 1, name: 'Item 1', price: 10, quantity: 1, total: 10 },
{ id: 2, name: 'Item 2', price: 15, quantity: 2, total: 30 },
{ id: 3, name: 'Item 3', price: 20, quantity: 1, total: 20 }
];
updateItemTotal(item: any): void {
item.total = item.price * item.quantity;
}
get orderTotal(): number {
return this.orderItems.reduce((sum, item) => sum + item.total, 0);
}
}Example 6: With Min/Max Enforcement
// Component
@Component({
selector: 'app-constrained-qty',
template: `
<label>Select Quantity (1-100):</label>
<ngx-st-qty-input
[(qtyModel)]="quantity"
[showError]="isOutOfRange"
(newValueEmitter)="enforceRange($event)">
</ngx-st-qty-input>
<p *ngIf="isOutOfRange" class="warning">
Quantity must be between 1 and 100
</p>
`
})
export class ConstrainedQtyComponent {
quantity = 1;
minValue = 1;
maxValue = 100;
isOutOfRange = false;
enforceRange(value: number): void {
this.isOutOfRange = false;
if (value < this.minValue) {
this.quantity = this.minValue;
this.isOutOfRange = true;
} else if (value > this.maxValue) {
this.quantity = this.maxValue;
this.isOutOfRange = true;
}
}
}Example 7: Real-time Price Calculator
// Component
@Component({
selector: 'app-price-calculator',
template: `
<div class="calculator">
<h3>{{ product.name }}</h3>
<img [src]="product.image" [alt]="product.name">
<div class="price-info">
<p>Unit Price: {{ product.price | currency }}</p>
<div class="qty-selector">
<label>Quantity:</label>
<ngx-st-qty-input
[(qtyModel)]="quantity"
[disabled]="!product.inStock"
(newValueEmitter)="calculatePrice($event)">
</ngx-st-qty-input>
</div>
<div class="discount" *ngIf="discount > 0">
<p>Discount: -{{ discount | currency }}</p>
</div>
<div class="total">
<h2>Total: {{ totalPrice | currency }}</h2>
</div>
<button
[disabled]="!product.inStock"
(click)="addToCart()">
Add to Cart
</button>
</div>
</div>
`
})
export class PriceCalculatorComponent {
product = {
id: 1,
name: 'Premium Widget',
price: 29.99,
image: 'widget.jpg',
inStock: true
};
quantity = 1;
totalPrice = 29.99;
discount = 0;
calculatePrice(qty: number): void {
const subtotal = this.product.price * qty;
// Apply bulk discounts
if (qty >= 10) {
this.discount = subtotal * 0.1; // 10% off
} else if (qty >= 5) {
this.discount = subtotal * 0.05; // 5% off
} else {
this.discount = 0;
}
this.totalPrice = subtotal - this.discount;
}
addToCart(): void {
console.log(`Adding ${this.quantity} items to cart`);
// Add to cart logic
}
}Example 8: Customizing Input Width
// Component
@Component({
selector: 'app-customizable-qty',
template: `
<div class="qty-variants">
<div class="compact">
<label>Compact (55px):</label>
<ngx-st-qty-input
[(qtyModel)]="quantity1"
[inputWidth]="'55px'">
</ngx-st-qty-input>
</div>
<div class="medium">
<label>Medium (80px):</label>
<ngx-st-qty-input
[(qtyModel)]="quantity2"
[inputWidth]="'80px'">
</ngx-st-qty-input>
</div>
<div class="large">
<label>Large (120px):</label>
<ngx-st-qty-input
[(qtyModel)]="quantity3"
[inputWidth]="'120px'">
</ngx-st-qty-input>
</div>
<div class="responsive">
<label>Full Width:</label>
<ngx-st-qty-input
[(qtyModel)]="quantity4"
[inputWidth]="'100%'">
</ngx-st-qty-input>
</div>
</div>
`
})
export class CustomizableQtyComponent {
quantity1 = 1;
quantity2 = 2;
quantity3 = 3;
quantity4 = 1;
}Example 9: Different Material Appearances
// Component
@Component({
selector: 'app-appearance-variants',
template: `
<div class="appearance-variants">
<div class="filled">
<label>Filled Appearance (Default):</label>
<ngx-st-qty-input
[(qtyModel)]="quantity1"
[appearance]="'fill'">
</ngx-st-qty-input>
</div>
<div class="outlined">
<label>Outlined Appearance:</label>
<ngx-st-qty-input
[(qtyModel)]="quantity2"
[appearance]="'outline'">
</ngx-st-qty-input>
</div>
</div>
`
})
export class AppearanceVariantsComponent {
quantity1 = 1;
quantity2 = 1;
}Example 10: With Stock Availability
// Component
@Component({
selector: 'app-stock-aware-qty',
template: `
<div class="product">
<h3>{{ product.name }}</h3>
<p>Available Stock: {{ product.stock }}</p>
<ngx-st-qty-input
[(qtyModel)]="quantity"
[showError]="exceedsStock"
[disabled]="product.stock === 0"
(newValueEmitter)="checkStock($event)">
</ngx-st-qty-input>
<p *ngIf="exceedsStock" class="error">
Only {{ product.stock }} items available
</p>
<p *ngIf="product.stock === 0" class="out-of-stock">
Out of Stock
</p>
<button
[disabled]="exceedsStock || product.stock === 0"
(click)="purchase()">
Purchase
</button>
</div>
`
})
export class StockAwareQtyComponent {
product = {
name: 'Limited Edition Item',
stock: 5
};
quantity = 1;
exceedsStock = false;
checkStock(qty: number): void {
if (qty > this.product.stock) {
this.exceedsStock = true;
// Optionally auto-correct
this.quantity = this.product.stock;
} else {
this.exceedsStock = false;
}
}
purchase(): void {
this.product.stock -= this.quantity;
console.log(`Purchased ${this.quantity} items`);
this.quantity = 1;
}
}Best Practices
Set appropriate initial values:
quantity = 1; // Don't start with 0 for shopping cartsUse validation to enforce business rules:
onQuantityChange(qty: number): void { if (qty > maxStock) { this.quantity = maxStock; } }Disable when not applicable:
[disabled]="outOfStock || isProcessing"Show errors for invalid values:
[showError]="quantity < minRequired || quantity > maxAllowed"Use
allowNegativeonly when needed:<!-- For adjustments --> [allowNegative]="true" <!-- For quantities (default) --> [allowNegative]="false"Provide feedback on changes:
onQuantityChange(qty: number): void { this.updateTotal(); this.checkAvailability(); this.calculateShipping(); }Consider debouncing for API calls:
onQuantityChange = debounce((qty: number) => { this.updateCartOnServer(qty); }, 500);
Component Behavior
Input Field Design
- Spinner Arrows Hidden: The native HTML number input spinner arrows (up/down) are intentionally hidden for a cleaner UI design. Users should use the dedicated plus/minus buttons instead.
- Browser Compatibility: The hiding of spinner arrows is implemented using CSS that works across all modern browsers (Chrome, Firefox, Safari, Edge).
- Clean Aesthetic: This provides a unified, Material Design-compliant appearance without browser-specific controls.
Increment Button (+)
- Increases value by 1
- Emits
newValueEmitterevent - Disabled when component is disabled
Decrement Button (-)
- Decreases value by 1
- If
allowNegativeis false and value would go below 0, sets to 0 - Emits
newValueEmitterevent - Disabled when component is disabled
Direct Input
- Allows manual entry of numeric values
- Validates and corrects negative values if
allowNegativeis false - Emits
newValueEmitterevent on blur or enter
Common Use Cases
- Shopping Cart: Product quantity selection
- Inventory Management: Stock adjustments (with negative values)
- Order Forms: Quantity selection for multiple items
- Configurators: Selecting quantities of customizable items
- Bulk Operations: Specifying number of items to process
This documentation covers all inputs, outputs, and usage patterns for the Quantity Input component.
