range-selection-input
v15.0.3
Published
This is an Angular Module containing Components/Services using Material
Readme
Range Selection Input Component
Overview
The range-selection-input library provides a Material Design slider component that allows users to select numeric values or ranges through an intuitive draggable interface. It supports both single value sliders and dual-thumb range selection, configurable min/max values, step divisions, tick marks, and display formatting. Built with Angular Material slider components and implementing ControlValueAccessor for seamless form integration.
Core Capabilities
🎚️ Numeric Range Selection Interface
- Single Value Selection: Select one numeric value using a single thumb slider
- Dual Range Selection: Select a range using dual thumbs with minimum and maximum values
- Configurable Range: Set minimum, maximum, and step values
- Tick Mark Support: Optional tick marks for discrete value selection
- Display Formatting: Customizable value display with formatting functions
- Form Control Integration: Implements
ControlValueAccessorfor reactive forms - Validation Support: Native Angular validation system compatibility
- Material Design: Built on Angular Material slider foundation
🔧 Features
✅ ControlValueAccessor Implementation - Works with Angular forms
✅ Material Design Integration - Uses Angular Material components
✅ Single & Range Selection - Flexible selection modes
✅ Configurable Steps - Set increment/decrement values
✅ Tick Marks - Discrete value selection support
✅ Value Formatting - Custom display formatting
✅ Form Validation - Native validation integration
✅ Disabled State - Disable slider interaction
✅ Display Functions - Custom value presentation
Key Benefits
| Feature | Description | |---------|-------------| | Intuitive Selection | Drag-based numeric value selection | | Range Support | Select minimum and maximum values simultaneously | | Precise Control | Configurable step values for precise selection | | Visual Feedback | Tick marks and value display | | Form Integration | Seamless Angular form control integration | | Flexible Formatting | Custom value display formatting |
Summary
The range-selection-input library provides an intuitive numeric selection component with support for both single values and ranges, making it ideal for settings like price ranges, quantity selections, or any scenario requiring precise numeric input through a visual interface.
Quick Start Guide
Installation & Setup (2 minutes)
1. Import Module
// app.module.ts
import { RangeSelectionInputModule } from 'range-selection-input';
@NgModule({
imports: [
RangeSelectionInputModule
]
})
export class AppModule { }2. No Module Configuration Required
The RangeSelectionInputModule does not require global configuration. Components can be used immediately after module import.
Quick Examples
Example 1: Basic Single Value Slider
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-basic-range',
template: `
<app-range-selection-input
[formControl]="volumeControl"
[min]="0"
[max]="100"
[step]="1"
label="Volume Level"
[showTicks]="true">
</app-range-selection-input>
<div>Volume: {{ volumeControl.value }}</div>
`
})
export class BasicRangeComponent {
volumeControl = new FormControl(50);
}Example 2: Dual Range Slider (Price Range)
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-price-range',
template: `
<app-range-selection-input
[formControl]="priceControl"
[min]="0"
[max]="1000"
[step]="10"
[showTicks]="true"
label="Price Range ($)"
[displayFunction]="formatPrice">
</app-range-selection-input>
<div class="price-display">
<div *ngIf="priceControl.value">
<strong>Selected Range:</strong>
{{ formatPrice(priceControl.value.min) }} - {{ formatPrice(priceControl.value.max) }}
</div>
</div>
`,
styles: [`
.price-display {
margin-top: 1rem;
padding: 1rem;
background: #f5f5f5;
border-radius: 4px;
font-size: 1.1rem;
}
`]
})
export class PriceRangeComponent {
priceControl = new FormControl({ min: 100, max: 500 });
formatPrice(value: number): string {
return `$${value.toFixed(0)}`;
}
}Example 3: Quantity Selection with Validation
import { Component } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-quantity-selector',
template: `
<app-range-selection-input
[formControl]="quantityControl"
[min]="1"
[max]="10"
[step]="1"
[showTicks]="true"
label="Select Quantity"
[required]="true">
</app-range-selection-input>
<div class="errors" *ngIf="quantityControl.errors && quantityControl.touched">
<div *ngIf="quantityControl.hasError('required')">
Quantity selection is required
</div>
<div *ngIf="quantityControl.hasError('min')">
Minimum quantity is 1
</div>
<div *ngIf="quantityControl.hasError('max')">
Maximum quantity is 10
</div>
</div>
<div class="quantity-info">
Quantity: {{ quantityControl.value }}
<span *ngIf="quantityControl.value">
(Total: ${{ (quantityControl.value * unitPrice).toFixed(2) }})
</span>
</div>
`,
styles: [`
.errors {
color: #f44336;
font-size: 0.875rem;
margin-top: 0.5rem;
}
.quantity-info {
margin-top: 1rem;
padding: 0.75rem;
background: #e8f5e8;
border: 1px solid #4caf50;
border-radius: 4px;
color: #2e7d32;
}
`]
})
export class QuantitySelectorComponent {
quantityControl = new FormControl(1, [
Validators.required,
Validators.min(1),
Validators.max(10)
]);
unitPrice = 29.99;
}Example 4: Percentage Selection
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-percentage-selector',
template: `
<app-range-selection-input
[formControl]="opacityControl"
[min]="0"
[max]="100"
[step]="5"
[showTicks]="true"
label="Opacity Level"
[displayFunction]="formatPercentage">
</app-range-selection-input>
<div class="preview-container">
<div
class="preview-box"
[style.opacity]="(opacityControl.value || 50) / 100">
<p>Preview Box</p>
<p>Opacity: {{ formatPercentage(opacityControl.value || 50) }}</p>
</div>
</div>
`,
styles: [`
.preview-container {
margin-top: 2rem;
display: flex;
justify-content: center;
}
.preview-box {
width: 200px;
height: 100px;
background: linear-gradient(45deg, #2196f3, #21cbf3);
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
text-shadow: 0 1px 2px rgba(0,0,0,0.5);
transition: opacity 0.3s ease;
}
.preview-box p {
margin: 0.25rem 0;
}
`]
})
export class PercentageSelectorComponent {
opacityControl = new FormControl(75);
formatPercentage(value: number): string {
return `${value}%`;
}
}Example 5: Temperature Range Selection
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-temperature-range',
template: `
<app-range-selection-input
[formControl]="temperatureControl"
[min]="-10"
[max]="40"
[step]="1"
[showTicks]="true"
label="Temperature Range (°C)"
[displayFunction]="formatTemperature">
</app-range-selection-input>
<div class="temperature-info" [attr.data-range]="getTemperatureRange()">
<h4>Temperature Range</h4>
<p>{{ formatTemperature(temperatureControl.value.min) }} to {{ formatTemperature(temperatureControl.value.max) }}</p>
<div class="range-description">
{{ getTemperatureDescription() }}
</div>
</div>
`,
styles: [`
.temperature-info {
margin-top: 1rem;
padding: 1rem;
border-radius: 8px;
text-align: center;
transition: all 0.3s ease;
}
.temperature-info[data-range="cold"] {
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
border: 2px solid #2196f3;
color: #1565c0;
}
.temperature-info[data-range="moderate"] {
background: linear-gradient(135deg, #fff3e0, #ffcc02);
border: 2px solid #ff9800;
color: #ef6c00;
}
.temperature-info[data-range="warm"] {
background: linear-gradient(135deg, #ffebee, #ef5350);
border: 2px solid #f44336;
color: #c62828;
}
.range-description {
margin-top: 0.5rem;
font-style: italic;
opacity: 0.8;
}
`]
})
export class TemperatureRangeComponent {
temperatureControl = new FormControl({ min: 15, max: 25 });
formatTemperature(value: number): string {
if (value < 0) {
return `${Math.abs(value)}°C below freezing`;
}
return `${value}°C`;
}
getTemperatureRange(): string {
const avg = (this.temperatureControl.value.min + this.temperatureControl.value.max) / 2;
if (avg < 10) return 'cold';
if (avg < 25) return 'moderate';
return 'warm';
}
getTemperatureDescription(): string {
const range = this.getTemperatureRange();
const descriptions = {
cold: 'Cool weather - jacket recommended',
moderate: 'Pleasant temperature',
warm: 'Warm weather - stay hydrated'
};
return descriptions[range];
}
}Example 6: Form Integration with Dynamic Range
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-dynamic-range-form',
template: `
<form [formGroup]="dynamicForm">
<app-range-selection-input
formControlName="age"
[min]="18"
[max]="100"
[step]="1"
[showTicks]="true"
label="Age"
placeholder="Select age"
[required]="true">
</app-range-selection-input>
<app-range-selection-input
formControlName="budget"
[min]="budgetRange.min"
[max]="budgetRange.max"
[step]="budgetRange.step"
[showTicks]="true"
label="Budget Range ($)"
[displayFunction]="formatCurrency">
</app-range-selection-input>
<app-range-selection-input
formControlName="rating"
[min]="0"
[max]="5"
[step]="0.5"
[showTicks]="true"
label="Rating"
[displayFunction]="formatStars">
</app-range-selection-input>
</form>
<div class="form-status">
<div>Form Valid: {{ dynamicForm.valid }}</div>
<div>Age: {{ dynamicForm.get('age')?.value }}</div>
<div>Budget: {{ formatCurrency(dynamicForm.get('budget')?.value?.min) }} - {{ formatCurrency(dynamicForm.get('budget')?.value?.max) }}</div>
<div>Rating: {{ formatStars(dynamicForm.get('rating')?.value) }}</div>
</div>
<div class="controls">
<button (click)="setDefaults()">Set Defaults</button>
<button (click)="resetForm()">Reset</button>
<button (click)="submitForm()" [disabled]="dynamicForm.invalid">Submit</button>
</div>
`,
styles: [`
form {
display: flex;
flex-direction: column;
gap: 2rem;
max-width: 400px;
}
.form-status {
margin-top: 1rem;
padding: 1rem;
background: #f5f5f5;
border-radius: 4px;
font-family: monospace;
font-size: 0.9rem;
}
.controls {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
}
button {
padding: 0.5rem 1rem;
border: 1px solid #ccc;
border-radius: 4px;
background: white;
cursor: pointer;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`]
})
export class DynamicRangeFormComponent {
dynamicForm: FormGroup;
budgetRange = { min: 1000, max: 10000, step: 100 };
constructor(private fb: FormBuilder) {
this.dynamicForm = this.fb.group({
age: [25, [Validators.required, Validators.min(18), Validators.max(100)]],
budget: [{ min: 3000, max: 7000 }, [Validators.required]],
rating: [3.5, [Validators.required, Validators.min(0), Validators.max(5)]]
});
}
formatCurrency(value: number): string {
if (typeof value === 'object' && value !== null) {
return `$${value.min?.toLocaleString()} - $${value.max?.toLocaleString()}`;
}
return `$${value.toLocaleString()}`;
}
formatStars(value: number): string {
const fullStars = Math.floor(value);
const hasHalfStar = value % 1 >= 0.5;
const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
return '★'.repeat(fullStars) +
(hasHalfStar ? '☆' : '') +
'☆'.repeat(emptyStars) +
` (${value})`;
}
setDefaults() {
this.dynamicForm.patchValue({
age: 30,
budget: { min: 2500, max: 8000 },
rating: 4.0
});
}
resetForm() {
this.dynamicForm.reset();
}
submitForm() {
if (this.dynamicForm.valid) {
console.log('Form submitted:', this.dynamicForm.value);
alert('Form submitted successfully!');
}
}
}Component API
Inputs
| Input | Type | Description | Default |
| :--- | :--- | :--- | :--- |
| min | number | Minimum value for the slider | 0 |
| max | number | Maximum value for the slider | 100 |
| step | number | Step increment/decrement value | 1 |
| showTicks | boolean | Whether to show tick marks | false |
| label | string | Label text for the slider | undefined |
| placeholder | string | Placeholder text for the slider | undefined |
| displayFunction | (value: number) => string | Function to format display values | undefined |
| required | boolean | Whether selection is required | false |
| disabled | boolean | Whether the slider is disabled | false |
Outputs
| Output | Type | Description |
|--------|------|-------------|
| valueChange | EventEmitter<number \| { min: number; max: number }> | Emits when slider value changes |
Form Integration (ControlValueAccessor)
The component implements Angular's ControlValueAccessor interface for seamless form integration.
ControlValueAccessor Implementation
// writeValue(value: number | { min: number; max: number }): void
// Sets the slider value(s)
writeValue(value: number | { min: number; max: number }): void {
this.selectedValue = value;
}
// registerOnChange(fn: any): void
// Registers a callback for value changes
registerOnChange(fn: any): void {
this.onChange = fn;
}
// registerOnTouched(fn: any): void
// Registers a callback for touch events
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// setDisabledState(isDisabled: boolean): void
// Sets disabled state
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}Form Integration Examples
Reactive Forms
import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-reactive-range-form',
template: `
<form [formGroup]="rangeForm">
<app-range-selection-input
formControlName="singleValue"
[min]="0"
[max]="100"
[step]="5"
[showTicks]="true"
label="Single Value"
[required]="true">
</app-range-selection-input>
<app-range-selection-input
formControlName="rangeValue"
[min]="0"
[max]="1000"
[step]="25"
[showTicks]="true"
label="Range Value"
[required]="true">
</app-range-selection-input>
</form>
`
})
export class ReactiveRangeFormComponent {
rangeForm = new FormGroup({
singleValue: new FormControl(50, [Validators.required, Validators.min(0), Validators.max(100)]),
rangeValue: new FormControl({ min: 100, max: 500 }, [Validators.required])
});
}Template-Driven Forms
import { Component } from '@angular/core';
@Component({
selector: 'app-template-range-form',
template: `
<app-range-selection-input
[(ngModel)]="selectedValue"
name="templateRange"
[min]="0"
[max]="100"
[step]="10"
[showTicks]="true"
label="Template Range Selection"
required>
</app-range-selection-input>
<div *ngIf="selectedValue">
Selected: {{ selectedValue }}
</div>
`
})
export class TemplateRangeFormComponent {
selectedValue = 25;
}Module Configuration
RangeSelectionInputModule
No Global Configuration Required
The RangeSelectionInputModule does not provide a forRoot() method or global configuration options. All configuration is done at the component level through input properties.
Dependencies
- @angular/core: Core Angular functionality
- @angular/forms: Form control integration (FormsModule, ReactiveFormsModule)
- @angular/material: Material Design components (MatSliderModule, MatFormFieldModule, etc.)
Advanced Usage Patterns
Custom Display Formatting
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-custom-formatting',
template: `
<app-range-selection-input
[formControl]="progressControl"
[min]="0"
[max]="100"
[step]="5"
[showTicks]="true"
label="Project Progress"
[displayFunction]="formatProgress">
</app-range-selection-input>
<div class="progress-visual">
<div class="progress-bar">
<div
class="progress-fill"
[style.width.%]="progressControl.value || 0">
</div>
</div>
<span class="progress-text">{{ formatProgress(progressControl.value || 0) }}</span>
</div>
`,
styles: [`
.progress-visual {
margin-top: 2rem;
}
.progress-bar {
width: 100%;
height: 20px;
background: #e0e0e0;
border-radius: 10px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #4caf50, #8bc34a);
transition: width 0.3s ease;
}
.progress-text {
font-weight: bold;
color: #333;
}
`]
})
export class CustomFormattingComponent {
progressControl = new FormControl(25);
formatProgress(value: number): string {
const percentage = Math.round(value);
const status = percentage < 25 ? 'Just Started' :
percentage < 50 ? 'Early Progress' :
percentage < 75 ? 'Halfway There' :
percentage < 90 ? 'Almost Done' : 'Complete';
return `${percentage}% - ${status}`;
}
}Dynamic Range Updates
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-dynamic-range',
template: `
<app-range-selection-input
[formControl]="difficultyControl"
[min]="difficultyRange.min"
[max]="difficultyRange.max"
[step]="difficultyRange.step"
[showTicks]="true"
label="Difficulty Level"
[displayFunction]="formatDifficulty">
</app-range-selection-input>
<div class="difficulty-info">
<h4>Difficulty: {{ formatDifficulty(difficultyControl.value || difficultyRange.min) }}</h4>
<p>{{ getDifficultyDescription() }}</p>
</div>
<div class="range-controls">
<label>
Quick Presets:
<select (change)="setPreset($event.target.value)">
<option value="">Choose preset</option>
<option value="easy">Easy (1-3)</option>
<option value="medium">Medium (4-6)</option>
<option value="hard">Hard (7-9)</option>
</select>
</label>
</div>
`,
styles: [`
.difficulty-info {
margin-top: 1rem;
padding: 1rem;
border-radius: 8px;
text-align: center;
}
.difficulty-info[data-level="easy"] {
background: #e8f5e8;
border: 2px solid #4caf50;
color: #2e7d32;
}
.difficulty-info[data-level="medium"] {
background: #fff3e0;
border: 2px solid #ff9800;
color: #ef6c00;
}
.difficulty-info[data-level="hard"] {
background: #ffebee;
border: 2px solid #f44336;
color: #c62828;
}
.range-controls {
margin-top: 1rem;
}
.range-controls select {
margin-left: 0.5rem;
padding: 0.25rem;
}
`]
})
export class DynamicRangeComponent {
difficultyControl = new FormControl(5);
difficultyRange = { min: 1, max: 10, step: 1 };
formatDifficulty(value: number): string {
if (value <= 3) return `Easy (${value})`;
if (value <= 6) return `Medium (${value})`;
return `Hard (${value})`;
}
getDifficultyDescription(): string {
const value = this.difficultyControl.value || this.difficultyRange.min;
if (value <= 3) return 'Simple and straightforward tasks';
if (value <= 6) return 'Moderate complexity with some challenges';
return 'Complex tasks requiring significant effort';
}
setPreset(preset: string) {
const presets = {
easy: { min: 1, max: 3 },
medium: { min: 4, max: 6 },
hard: { min: 7, max: 9 }
};
if (preset && presets[preset as keyof typeof presets]) {
const range = presets[preset as keyof typeof presets];
this.difficultyControl.setValue(range.min);
this.difficultyRange = { ...this.difficultyRange, ...range };
}
}
}Performance Optimization
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-optimized-range',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<app-range-selection-input
[formControl]="optimizedControl"
[min]="0"
[max]="1000000"
[step]="1000"
[showTicks]="true"
label="Large Range Selection"
[displayFunction]="formatLargeNumber">
</app-range-selection-input>
`
})
export class OptimizedRangeComponent {
optimizedControl = new FormControl(500000);
formatLargeNumber(value: number): string {
if (value >= 1000000) {
return `${(value / 1000000).toFixed(1)}M`;
} else if (value >= 1000) {
return `${(value / 1000).toFixed(0)}K`;
}
return value.toString();
}
}Integration Examples
With Other UI Components
import { Component } from '@angular/core';
@Component({
selector: 'app-integrated-range',
template: `
<app-display-card title="Price Configuration">
<app-range-selection-input
[formControl]="priceControl"
[min]="0"
[max]="1000"
[step]="10"
[showTicks]="true"
label="Price Range"
[displayFunction]="formatPrice">
</app-range-selection-input>
<div *ngIf="priceControl.value" class="price-summary">
<h4>Price Configuration</h4>
<p><strong>Selected Range:</strong> {{ formatPrice(priceControl.value.min) }} - {{ formatPrice(priceControl.value.max) }}</p>
<p><strong>Average Price:</strong> {{ formatPrice((priceControl.value.min + priceControl.value.max) / 2) }}</p>
<p><strong>Price Spread:</strong> {{ formatPrice(priceControl.value.max - priceControl.value.min) }}</p>
</div>
</app-display-card>
`
})
export class IntegratedRangeComponent {
priceControl = new FormControl({ min: 100, max: 500 });
formatPrice(value: number): string {
if (typeof value === 'object' && value !== null) {
return `$${value.min?.toFixed(0)} - $${value.max?.toFixed(0)}`;
}
return `$${value.toFixed(0)}`;
}
}With State Management
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
@Component({
selector: 'app-state-range',
template: `
<app-range-selection-input
[formControl]="volumeControl"
[data]="volumeOptions$ | async"
label="Audio Volume"
placeholder="Adjust volume"
(valueChange)="onVolumeChange($event)">
</app-range-selection-input>
`
})
export class StateRangeComponent {
volumeControl = new FormControl();
volumeOptions$ = this.store.select(state => state.volumeOptions);
constructor(private store: Store) {}
onVolumeChange(volume: number) {
this.store.dispatch(adjustVolume({ volume }));
}
}Testing
Unit Testing Example
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RangeSelectionInputComponent } from './range-selection-input.component';
import { ReactiveFormsModule } from '@angular/forms';
describe('RangeSelectionInputComponent', () => {
let component: RangeSelectionInputComponent;
let fixture: ComponentFixture<RangeSelectionInputComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RangeSelectionInputComponent ],
imports: [ ReactiveFormsModule ]
}).compileComponents();
fixture = TestBed.createComponent(RangeSelectionInputComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should handle single value selection', () => {
component.min = 0;
component.max = 100;
component.step = 1;
fixture.detectChanges();
component.writeValue(50);
expect(component.selectedValue).toBe(50);
});
it('should handle range value selection', () => {
component.min = 0;
component.max = 100;
component.step = 1;
fixture.detectChanges();
component.writeValue({ min: 25, max: 75 });
expect(component.selectedValue).toEqual({ min: 25, max: 75 });
});
it('should emit value changes', () => {
spyOn(component.valueChange, 'emit');
component.onValueChange(50);
expect(component.valueChange.emit).toHaveBeenCalledWith(50);
});
it('should handle form control integration', () => {
const formControl = new FormControl();
component.formControl = formControl;
spyOn(formControl, 'setValue');
component.writeValue(75);
expect(formControl.setValue).toHaveBeenCalledWith(75);
});
it('should apply custom display formatting', () => {
component.displayFunction = (value: number) => `${value}%`;
fixture.detectChanges();
const formattedValue = component.displayFunction(50);
expect(formattedValue).toBe('50%');
});
});Troubleshooting
Common Issues
- Form control not working: Ensure ReactiveFormsModule is imported
- Slider not responding: Check min/max/step values are properly configured
- Display formatting not working: Verify displayFunction is properly defined
- Range selection not working: Ensure dual-thumb mode is properly configured
- Styling issues: Verify Material theme is properly configured
Debug Mode
@Component({
template: `
<div class="debug-info">
Form Control Value: {{ formControl.value | json }}<br>
Form Control Valid: {{ formControl.valid }}<br>
Min: {{ min }}<br>
Max: {{ max }}<br>
Step: {{ step }}<br>
Show Ticks: {{ showTicks }}<br>
Selected Value: {{ selectedValue | json }}
</div>
<app-range-selection-input
[formControl]="formControl"
[min]="min"
[max]="max"
[step]="step"
[showTicks]="showTicks"
[displayFunction]="displayFunction"
(valueChange)="onValueChange($event)">
</app-range-selection-input>
`
})
export class DebugRangeComponent {
formControl = new FormControl();
min = 0;
max = 100;
step = 1;
showTicks = false;
selectedValue: any = null;
displayFunction = (value: number) => `Value: ${value}`;
constructor() {
this.formControl.valueChanges.subscribe(value => {
console.log('Range value changed:', value);
});
this.formControl.statusChanges.subscribe(status => {
console.log('Form control status:', status);
});
}
onValueChange(value: any) {
this.selectedValue = value;
console.log('Value changed:', value);
}
}Performance Monitoring
@Component({
template: `
<div class="performance-info">
Range Size: {{ max - min }}<br>
Step Count: {{ (max - min) / step }}<br>
Update Time: {{ updateTime }}ms<br>
Change Operations: {{ changeOperationCount }}
</div>
<app-range-selection-input
[formControl]="formControl"
[min]="min"
[max]="max"
[step]="step"
(valueChange)="onValueChange($event)">
</app-range-selection-input>
`
})
export class PerformanceDebugComponent {
formControl = new FormControl();
min = 0;
max = 1000;
step = 1;
updateTime = 0;
changeOperationCount = 0;
onValueChange(value: any) {
const start = performance.now();
// Perform value update operation
this.processValueUpdate(value);
const end = performance.now();
this.updateTime = end - start;
this.changeOperationCount++;
}
private processValueUpdate(value: any) {
// Your value processing logic
}
}Accessibility Features
ARIA Support
- Sliders include proper ARIA labels and roles
- Keyboard navigation is fully supported (Arrow keys, Page Up/Down, Home/End)
- Screen reader friendly with appropriate descriptions
- Focus management for keyboard users
- Value announcements for screen readers
Keyboard Navigation
| Key | Action |
|-----|--------|
| Arrow Keys | Adjust slider value by step |
| Page Up/Down | Adjust by larger increments |
| Home | Set to minimum value |
| End | Set to maximum value |
| Space | Toggle selection (if applicable) |
Best Practices
- Provide meaningful labels for slider accessibility
- Set appropriate min/max values for context
- Use sensible step values for precision
- Test with screen readers to ensure proper announcements
- Consider keyboard users for navigation
- Use consistent styling for better visual accessibility
