list-selection-input
v15.0.5
Published
This is an Angular Module containing Components/Services using Material
Readme
List Selection Input Component
Overview
The list-selection-input library provides a Material Design dropdown selection component that allows users to select from a list of options in a space-efficient manner. It supports both single and multiple selection modes, validation limits, disabled items, and works with both string and object data formats. Built with Angular Material select components and implementing ControlValueAccessor for seamless form integration.
Core Capabilities
📋 Dropdown List Selection Interface
- Single/Multiple Selection: Support for both single selection and multi-select modes
- Form Validation: Built-in min/max selection validation with error handling
- Flexible Data Support: Handles both string arrays and complex object arrays
- Disabled State Support: Mark individual items as non-selectable
- Material Design: Built on Angular Material select foundation
- Form Control Integration: Implements
ControlValueAccessorfor reactive forms - Validation Integration: Native Angular validation system compatibility
- Space Efficient: Dropdown interface saves screen space
🔧 Features
✅ ControlValueAccessor Implementation - Works with Angular forms
✅ Material Design Integration - Uses Angular Material components
✅ Single & Multiple Selection - Flexible selection modes
✅ Min/Max Validation - Configurable selection limits
✅ Disabled Items - Mark individual options as non-selectable
✅ Flexible Data Types - Support for strings and objects
✅ Pre-selection - Initialize with selected items
✅ Form Validation - Native validation integration
✅ Dropdown Interface - Space-efficient selection display
Key Benefits
| Feature | Description | |---------|-------------| | Space Efficient | Compact dropdown interface vs. visible lists | | Flexible Selection | Support for single or multiple item selection | | Rich Validation | Built-in min/max selection limits with error states | | Data Format Support | Works with simple strings or complex objects | | State Management | Disabled states and pre-selection capabilities | | Form Integration | Seamless Angular form control and validation integration |
Summary
The list-selection-input library provides a space-efficient dropdown selection component with comprehensive form integration, validation support, and multiple selection capabilities for Angular applications.
Quick Start Guide
Installation & Setup (2 minutes)
1. Import Module
// app.module.ts
import { ListSelectionInputModule } from 'list-selection-input';
@NgModule({
imports: [
ListSelectionInputModule
]
})
export class AppModule { }2. No Module Configuration Required
The ListSelectionInputModule does not require global configuration. Components can be used immediately after module import.
Quick Examples
Example 1: Basic Single Selection
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-basic-list-select',
template: `
<app-list-selection-input
[formControl]="selectionControl"
[data]="options">
</app-list-selection-input>
<div>Selected: {{ selectionControl.value }}</div>
`
})
export class BasicListSelectComponent {
selectionControl = new FormControl();
options = ['Option 1', 'Option 2', 'Option 3', 'Option 4'];
}Example 2: Multiple Selection with Validation
import { Component } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-multi-list-select',
template: `
<app-list-selection-input
[formControl]="multiControl"
[data]="skills"
[multiple]="true"
[minSelection]="2"
[maxSelection]="4"
label="Select Skills"
placeholder="Choose your skills">
</app-list-selection-input>
<div class="errors" *ngIf="multiControl.errors && multiControl.touched">
<div *ngIf="multiControl.hasError('minRequired')">
Please select at least 2 skills
</div>
<div *ngIf="multiControl.hasError('maxExceeded')">
You can select maximum 4 skills
</div>
</div>
`,
styles: [`
.errors {
color: #f44336;
font-size: 0.875rem;
margin-top: 0.5rem;
}
`]
})
export class MultiListSelectComponent {
multiControl = new FormControl([], [
Validators.required,
Validators.minLength(2),
Validators.maxLength(4)
]);
skills = [
'JavaScript', 'TypeScript', 'Python', 'Java', 'C#', 'PHP', 'Ruby', 'Go',
'Rust', 'Swift', 'Kotlin', 'Dart', 'Scala', 'Clojure', 'Elixir', 'Haskell'
];
}Example 3: Object Data with Disabled Items
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-object-list-select',
template: `
<app-list-selection-input
[formControl]="userControl"
[data]="userOptions"
label="Assign User"
placeholder="Select a user"
displayField="label">
</app-list-selection-input>
<div *ngIf="userControl.value" class="user-info">
<h4>{{ userControl.value.label }}</h4>
<p>{{ userControl.value.email }}</p>
<p>Role: {{ userControl.value.role }}</p>
</div>
`,
styles: [`
.user-info {
margin-top: 1rem;
padding: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
background: #f9f9f9;
}
.user-info h4 {
margin: 0 0 0.5rem 0;
color: #333;
}
.user-info p {
margin: 0.25rem 0;
font-size: 0.9rem;
color: #666;
}
`]
})
export class ObjectListSelectComponent {
userControl = new FormControl();
userOptions = [
{ value: 'user1', label: 'John Doe', email: '[email protected]', role: 'Admin', disabled: false },
{ value: 'user2', label: 'Jane Smith', email: '[email protected]', role: 'Editor', disabled: false },
{ value: 'user3', label: 'Bob Johnson', email: '[email protected]', role: 'Viewer', disabled: true },
{ value: 'user4', label: 'Alice Brown', email: '[email protected]', role: 'Editor', disabled: false },
{ value: 'user5', label: 'Charlie Wilson', email: '[email protected]', role: 'Admin', disabled: false }
];
}Example 4: Multi-Select with Pre-selection
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-pre-selected-list',
template: `
<app-list-selection-input
[formControl]="preSelectedControl"
[data]="preSelectedOptions"
[multiple]="true"
label="Favorite Languages"
placeholder="Select your favorite programming languages">
</app-list-selection-input>
<div class="selection-info">
Selected ({{ preSelectedControl.value?.length || 0 }}):
{{ preSelectedControl.value?.join(', ') || 'None' }}
</div>
`,
styles: [`
.selection-info {
margin-top: 1rem;
padding: 0.75rem;
background: #e8f5e8;
border: 1px solid #4caf50;
border-radius: 4px;
color: #2e7d32;
font-size: 0.9rem;
}
`]
})
export class PreSelectedListComponent {
preSelectedControl = new FormControl(['JavaScript', 'TypeScript']);
preSelectedOptions = [
{ value: 'JavaScript', label: 'JavaScript', selected: true },
{ value: 'TypeScript', label: 'TypeScript', selected: true },
{ value: 'Python', label: 'Python', selected: false },
{ value: 'Java', label: 'Java', selected: false },
{ value: 'C#', label: 'C#', selected: false },
{ value: 'PHP', label: 'PHP', selected: false },
{ value: 'Ruby', label: 'Ruby', selected: false },
{ value: 'Go', label: 'Go', selected: false }
];
}Example 5: Form Integration with Dynamic Options
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-dynamic-list-form',
template: `
<form [formGroup]="dynamicForm">
<app-list-selection-input
formControlName="category"
[data]="categoryOptions"
label="Product Category"
placeholder="Select a category"
[required]="true">
</app-list-selection-input>
<app-list-selection-input
formControlName="subcategories"
[data]="subcategoryOptions"
label="Subcategories"
placeholder="Select subcategories"
[multiple]="true"
[minSelection]="1"
[maxSelection]="3"
[disabled]="!dynamicForm.get('category')?.value">
</app-list-selection-input>
</form>
<div class="form-status">
<div>Form Valid: {{ dynamicForm.valid }}</div>
<div>Category: {{ dynamicForm.get('category')?.value }}</div>
<div>Subcategories: {{ dynamicForm.get('subcategories')?.value?.length || 0 }} selected</div>
</div>
<div class="controls">
<button (click)="resetForm()">Reset</button>
<button (click)="setDefaults()">Set Defaults</button>
<button (click)="submitForm()" [disabled]="dynamicForm.invalid">Submit</button>
</div>
`,
styles: [`
form {
display: flex;
flex-direction: column;
gap: 1rem;
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 DynamicListFormComponent {
dynamicForm: FormGroup;
categoryOptions = [
'Electronics', 'Clothing', 'Books', 'Home & Garden', 'Sports & Outdoors',
'Automotive', 'Health & Beauty', 'Toys & Games', 'Food & Beverages'
];
subcategoryOptions: string[] = [];
constructor(private fb: FormBuilder) {
this.dynamicForm = this.fb.group({
category: ['', Validators.required],
subcategories: [[], [Validators.required, Validators.minLength(1), Validators.maxLength(3)]]
});
// Watch for category changes to update subcategories
this.dynamicForm.get('category')?.valueChanges.subscribe(category => {
this.updateSubcategories(category);
this.dynamicForm.get('subcategories')?.reset();
});
}
updateSubcategories(category: string) {
const subcategoryMap: { [key: string]: string[] } = {
'Electronics': ['Smartphones', 'Laptops', 'Tablets', 'Headphones', 'Cameras'],
'Clothing': ['Men\'s Wear', 'Women\'s Wear', 'Kids\' Wear', 'Accessories', 'Footwear'],
'Books': ['Fiction', 'Non-Fiction', 'Educational', 'Children', 'Comics'],
'Home & Garden': ['Furniture', 'Decor', 'Kitchen', 'Tools', 'Plants'],
'Sports & Outdoors': ['Exercise', 'Outdoor Gear', 'Sports Equipment', 'Team Sports', 'Water Sports']
};
this.subcategoryOptions = subcategoryMap[category] || [];
}
resetForm() {
this.dynamicForm.reset();
this.subcategoryOptions = [];
}
setDefaults() {
this.dynamicForm.patchValue({
category: 'Electronics',
subcategories: ['Smartphones', 'Laptops']
});
}
submitForm() {
if (this.dynamicForm.valid) {
console.log('Form submitted:', this.dynamicForm.value);
alert('Form submitted successfully!');
}
}
}Component API
Inputs
| Input | Type | Description | Default |
| :--- | :--- | :--- | :--- |
| data | any[] \| string[] | Array of options to select from | (Required) |
| multiple | boolean | Allow multiple selections | false |
| label | string | Label text for the select field | undefined |
| placeholder | string | Placeholder text for the select field | undefined |
| displayField | string | Property name to display for object arrays | undefined |
| minSelection | number | Minimum number of selections required | 0 |
| maxSelection | number | Maximum number of selections allowed | 0 |
| required | boolean | Whether selection is required | false |
| disabled | boolean | Whether the select is disabled | false |
Outputs
| Output | Type | Description |
|--------|------|-------------|
| selectionChange | EventEmitter<any[]> | Emits array of selected values when selection changes |
Form Integration (ControlValueAccessor)
The component implements Angular's ControlValueAccessor interface for seamless form integration.
ControlValueAccessor Implementation
// writeValue(value: any[]): void
// Sets the selected values
writeValue(value: any[]): void {
this.selectedValues = 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-list-form',
template: `
<form [formGroup]="listForm">
<app-list-selection-input
formControlName="country"
[data]="countries"
label="Country"
placeholder="Select a country"
[required]="true">
</app-list-selection-input>
<app-list-selection-input
formControlName="languages"
[data]="languages"
[multiple]="true"
label="Programming Languages"
placeholder="Select languages"
[minSelection]="1"
[maxSelection]="5">
</app-list-selection-input>
</form>
`
})
export class ReactiveListFormComponent {
listForm = new FormGroup({
country: new FormControl('', Validators.required),
languages: new FormControl([], [Validators.required, Validators.minLength(1), Validators.maxLength(5)])
});
countries = ['United States', 'Canada', 'United Kingdom', 'Germany', 'France', 'Japan'];
languages = ['JavaScript', 'Python', 'Java', 'C#', 'PHP', 'Ruby', 'Go', 'Rust'];
}Template-Driven Forms
import { Component } from '@angular/core';
@Component({
selector: 'app-template-list-form',
template: `
<app-list-selection-input
[(ngModel)]="selectedValue"
name="templateSelection"
[data]="templateOptions"
label="Template Selection"
placeholder="Choose an option"
[multiple]="true"
required>
</app-list-selection-input>
<div *ngIf="selectedValue">
Selected: {{ selectedValue.join(', ') }}
</div>
`
})
export class TemplateListFormComponent {
selectedValue: string[] = [];
templateOptions = ['Option A', 'Option B', 'Option C', 'Option D', 'Option E'];
}Model Structures
Selection Option Interface
export interface SelectionOptionInterface {
value: any; // The value to be selected/returned
label?: string; // Optional display label
disabled?: boolean; // Whether this option is disabled
selected?: boolean; // Whether this option is pre-selected
}Selection Option Class
export class SelectionOption implements SelectionOptionInterface {
constructor(
public value: any,
public label?: string,
public disabled?: boolean = false,
public selected?: boolean = false,
) {}
static adapt(item?: any): SelectionOption {
return new SelectionOption(
item?.value ?? item,
item?.label,
item?.disabled || false,
item?.selected || false,
);
}
}Usage Examples
// String array data (automatically converted)
const stringData = ['Option 1', 'Option 2', 'Option 3'];
// Object array data
const objectData = [
{ value: 'opt1', label: 'Option 1', disabled: false, selected: true },
{ value: 'opt2', label: 'Option 2', disabled: true, selected: false },
{ value: 'opt3', label: 'Option 3', disabled: false, selected: false }
];
// Manual SelectionOption creation
const manualOptions = [
new SelectionOption('value1', 'Label 1', false, true),
new SelectionOption('value2', 'Label 2', true, false),
new SelectionOption('value3', 'Label 3', false, false)
];
// Using adapt method for flexible data
const adaptedOptions = [
SelectionOption.adapt({ value: 'item1', label: 'Item 1', selected: true }),
SelectionOption.adapt({ value: 'item2', label: 'Item 2', disabled: true }),
SelectionOption.adapt('item3') // String fallback
];Validation System
Built-in Validation
The component provides built-in validation for:
Min Selection Validation
// Requires at least X selections
[minSelection]="2" // Must select at least 2 itemsMax Selection Validation
// Allows maximum X selections
[maxSelection]="3" // Cannot select more than 3 itemsRequired Validation
// Marks field as required
[required]="true" // At least one selection requiredValidation Error Types
| Error Type | Condition | Description |
|------------|-----------|-------------|
| minRequired | selectedCount < minSelection | Not enough selections made |
| maxExceeded | selectedCount >= maxSelection | Too many selections made |
| required | Control has required validator and no selections | Control is required but empty |
Custom Validation Examples
// Complex validation scenarios
const complexForm = new FormGroup({
permissions: new FormControl([], [
Validators.required,
Validators.minLength(1), // At least 1 selection
Validators.maxLength(5), // Maximum 5 selections
customPermissionValidator() // Custom validation logic
])
});
// Custom validator example
function customPermissionValidator() {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (value && value.includes('admin') && value.includes('delete')) {
return { adminDeleteConflict: true };
}
return null;
};
}Module Configuration
ListSelectionInputModule
No Global Configuration Required
The ListSelectionInputModule 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 (MatSelectModule, MatFormFieldModule, etc.)
Advanced Usage Patterns
Cascading Selections
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-cascading-selects',
template: `
<form [formGroup]="cascadingForm">
<app-list-selection-input
formControlName="country"
[data]="countries"
label="Country"
placeholder="Select a country">
</app-list-selection-input>
<app-list-selection-input
formControlName="state"
[data]="states"
label="State/Province"
placeholder="Select a state"
[disabled]="!cascadingForm.get('country')?.value">
</app-list-selection-input>
<app-list-selection-input
formControlName="city"
[data]="cities"
label="City"
placeholder="Select a city"
[disabled]="!cascadingForm.get('state')?.value">
</app-list-selection-input>
</form>
`
})
export class CascadingSelectsComponent {
cascadingForm: FormGroup;
states: string[] = [];
cities: string[] = [];
countries = ['United States', 'Canada', 'United Kingdom'];
stateData = {
'United States': ['California', 'New York', 'Texas', 'Florida'],
'Canada': ['Ontario', 'Quebec', 'British Columbia', 'Alberta'],
'United Kingdom': ['England', 'Scotland', 'Wales', 'Northern Ireland']
};
cityData = {
'California': ['Los Angeles', 'San Francisco', 'San Diego', 'Sacramento'],
'New York': ['New York City', 'Buffalo', 'Rochester', 'Albany'],
'Ontario': ['Toronto', 'Ottawa', 'Mississauga', 'Hamilton']
};
constructor(private fb: FormBuilder) {
this.cascadingForm = this.fb.group({
country: [''],
state: [''],
city: ['']
});
// Watch for country changes
this.cascadingForm.get('country')?.valueChanges.subscribe(country => {
this.states = this.stateData[country as keyof typeof this.stateData] || [];
this.cascadingForm.get('state')?.reset();
this.cascadingForm.get('city')?.reset();
});
// Watch for state changes
this.cascadingForm.get('state')?.valueChanges.subscribe(state => {
this.cities = this.cityData[state as keyof typeof this.cityData] || [];
this.cascadingForm.get('city')?.reset();
});
}
}Async Data Loading
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
@Component({
selector: 'app-async-list-select',
template: `
<app-list-selection-input
[formControl]="searchControl"
[data]="searchResults$ | async"
label="Search Products"
placeholder="Start typing to search..."
displayField="name"
[loading]="loading">
</app-list-selection-input>
<div *ngIf="loading" class="loading-indicator">
Loading products...
</div>
`
})
export class AsyncListSelectComponent {
searchControl = new FormControl();
searchResults$!: Observable<any[]>;
loading = false;
constructor(private http: HttpClient) {
this.searchResults$ = this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(query => this.searchProducts(query))
);
}
private searchProducts(query: string): Observable<any[]> {
this.loading = true;
return this.http.get<any[]>(`/api/products/search?q=${encodeURIComponent(query)}`).pipe(
// Simulate loading state
switchMap(results => {
this.loading = false;
return [results];
})
);
}
}Performance Optimization
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-optimized-list-select',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<app-list-selection-input
[formControl]="optimizedControl"
[data]="largeDataset"
label="Large Dataset Selection"
placeholder="Select from large dataset..."
[filterFunction]="customFilter">
</app-list-selection-input>
`
})
export class OptimizedListSelectComponent {
optimizedControl = new FormControl();
// Large dataset with efficient filtering
largeDataset = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Product ${i}`,
category: `Category ${Math.floor(i / 100)}`,
price: Math.random() * 1000
}));
customFilter(data: any[], filterText: string): any[] {
if (!filterText) return data;
const lowerFilter = filterText.toLowerCase();
return data.filter(item =>
item.name.toLowerCase().includes(lowerFilter) ||
item.category.toLowerCase().includes(lowerFilter)
);
}
}Integration Examples
With Other UI Components
import { Component } from '@angular/core';
@Component({
selector: 'app-integrated-list-select',
template: `
<app-display-card title="User Assignment">
<app-list-selection-input
[formControl]="userControl"
[data]="users"
label="Assign to User"
placeholder="Select a user"
displayField="name">
</app-list-selection-input>
<div *ngIf="userControl.value" class="assignment-info">
<h4>Assigned to: {{ userControl.value.name }}</h4>
<p>{{ userControl.value.email }}</p>
<p>Role: {{ userControl.value.role }}</p>
<p>Department: {{ userControl.value.department }}</p>
</div>
</app-display-card>
`
})
export class IntegratedListSelectComponent {
userControl = new FormControl();
users = [
{
id: 1,
name: 'John Doe',
email: '[email protected]',
role: 'Admin',
department: 'Engineering'
},
{
id: 2,
name: 'Jane Smith',
email: '[email protected]',
role: 'Editor',
department: 'Marketing'
}
];
}With State Management
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
@Component({
selector: 'app-state-list-select',
template: `
<app-list-selection-input
[formControl]="categoryControl"
[data]="categories$ | async"
label="Product Category"
placeholder="Select a category"
(selectionChange)="onCategorySelected($event)">
</app-list-selection-input>
`
})
export class StateListSelectComponent {
categoryControl = new FormControl();
categories$ = this.store.select(state => state.categories);
constructor(private store: Store) {}
onCategorySelected(category: any) {
this.store.dispatch(selectCategory({ category }));
}
}Testing
Unit Testing Example
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ListSelectionInputComponent } from './list-selection-input.component';
import { ReactiveFormsModule } from '@angular/forms';
describe('ListSelectionInputComponent', () => {
let component: ListSelectionInputComponent;
let fixture: ComponentFixture<ListSelectionInputComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ListSelectionInputComponent ],
imports: [ ReactiveFormsModule ]
}).compileComponents();
fixture = TestBed.createComponent(ListSelectionInputComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display options from data input', () => {
component.data = ['Option 1', 'Option 2', 'Option 3'];
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.textContent).toContain('Option 1');
expect(compiled.textContent).toContain('Option 2');
expect(compiled.textContent).toContain('Option 3');
});
it('should handle multiple selection mode', () => {
component.multiple = true;
component.data = ['Option 1', 'Option 2', 'Option 3'];
expect(component.multiple).toBe(true);
});
it('should emit selection changes', () => {
spyOn(component.selectionChange, 'emit');
component.onSelectionChange(['Option 1', 'Option 2']);
expect(component.selectionChange.emit).toHaveBeenCalledWith(['Option 1', 'Option 2']);
});
it('should validate minimum selections', () => {
component.minSelection = 2;
component.data = ['Option 1', 'Option 2', 'Option 3'];
// Simulate selecting only 1 item
component.writeValue(['Option 1']);
const validationResult = component.validate({} as AbstractControl);
expect(validationResult?.minRequired).toBe(true);
});
});Troubleshooting
Common Issues
- Form control not working: Ensure ReactiveFormsModule is imported
- Validation not triggering: Check that validators are properly configured
- Selection not updating: Verify data format matches expected structure
- Multiple selection not working: Check that
multipleinput is set totrue - Object display issues: Set
displayFieldproperty for object arrays - Styling issues: Ensure 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>
Form Control Errors: {{ formControl.errors | json }}<br>
Data Length: {{ data?.length || 0 }}<br>
Multiple Mode: {{ multiple }}<br>
Min Selection: {{ minSelection }}<br>
Max Selection: {{ maxSelection }}
</div>
<app-list-selection-input
[formControl]="formControl"
[data]="data"
[multiple]="multiple"
[minSelection]="minSelection"
[maxSelection]="maxSelection"
(selectionChange)="onSelectionChange($event)">
</app-list-selection-input>
`
})
export class DebugListSelectComponent {
formControl = new FormControl();
data: any[] = [];
multiple = false;
minSelection = 0;
maxSelection = 0;
constructor() {
this.formControl.valueChanges.subscribe(value => {
console.log('List selection value changed:', value);
});
this.formControl.statusChanges.subscribe(status => {
console.log('Form control status:', status);
});
}
onSelectionChange(selection: any[]) {
console.log('Selection changed:', selection);
}
}Performance Debugging
@Component({
template: `
<div class="performance-info">
Data Size: {{ data?.length || 0 }} items<br>
Render Time: {{ renderTime }}ms<br>
Filter Operations: {{ filterOperationCount }}
</div>
<app-list-selection-input
[formControl]="formControl"
[data]="data"
(selectionChange)="onSelectionChange($event)">
</app-list-selection-input>
`
})
export class PerformanceDebugComponent {
formControl = new FormControl();
data: any[] = [];
renderTime = 0;
filterOperationCount = 0;
onSelectionChange(selection: any[]) {
const start = performance.now();
// Perform selection operation
this.processSelection(selection);
const end = performance.now();
this.renderTime = end - start;
this.filterOperationCount++;
}
private processSelection(selection: any[]) {
// Your selection processing logic
}
}