npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

filter-selection-input

v15.0.5

Published

This is an Angular Module containing Components/Services using Material

Readme

Filter Selection Input Component

Overview

The filter-selection-input library provides a Material Design autocomplete selection component that allows users to select from a filtered list of options. It features real-time filtering as users type, force selection from provided options, and support for both strings and objects. Built with Angular Material and implementing ControlValueAccessor for seamless form integration.

Core Capabilities

🔍 Autocomplete Selection Interface

  • Real-time Filtering: Filters options as users type in the input field
  • Force Selection: Ensures users select from provided options only
  • Single Selection: Only allows selection from the filtered list (like radio buttons)
  • Flexible Data Support: Handles both string arrays and complex objects
  • Form Control Integration: Implements ControlValueAccessor for reactive forms
  • Material Design: Built on Angular Material autocomplete foundation
  • Customizable Display: Configurable labels and placeholders

🔧 Features

Real-time Filtering - Instant filtering as user types
Force Selection - User must select from provided options
Single Selection - Radio button-like exclusive selection
Material Design Integration - Uses Angular Material components
Form Control Support - ControlValueAccessor implementation
Flexible Data Types - Support for strings and objects
Customizable Labels - Configurable display labels
Placeholder Support - Helpful placeholder text

Key Benefits

| Feature | Description | |---------|-------------| | Type-Ahead Search | Users can quickly find options by typing | | Data Validation | Ensures selections are from the allowed options | | Space Efficient | Compact dropdown interface | | Form Integration | Seamless Angular form control integration | | User Friendly | Intuitive autocomplete experience |


Summary

The filter-selection-input library provides a type-ahead selection component that combines the benefits of autocomplete search with controlled selection options, making it ideal for large option lists where users need to find and select specific items.


Quick Start Guide

Installation & Setup (2 minutes)

1. Import Module

// app.module.ts
import { FilterSelectionInputModule } from 'filter-selection-input';

@NgModule({
  imports: [
    FilterSelectionInputModule
  ]
})
export class AppModule { }

2. No Module Configuration Required

The FilterSelectionInputModule does not require global configuration. Components can be used immediately after module import.

Quick Examples

Example 1: Basic String Array Selection

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-basic-filter',
  template: `
    <app-filter-selection-input
      [formControl]="countryControl"
      [data]="countries"
      label="Select Country"
      placeholder="Start typing to search...">
    </app-filter-selection-input>
    
    <div>Selected: {{ countryControl.value }}</div>
  `
})
export class BasicFilterComponent {
  countryControl = new FormControl();
  
  countries = [
    'United States', 'Canada', 'United Kingdom', 'Germany', 'France',
    'Italy', 'Spain', 'Australia', 'Japan', 'China', 'India', 'Brazil',
    'Mexico', 'Netherlands', 'Sweden', 'Norway', 'Denmark', 'Finland',
    'Switzerland', 'Austria', 'Belgium', 'Ireland', 'Portugal', 'Greece'
  ];
}

Example 2: Object Array Selection

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-object-filter',
  template: `
    <app-filter-selection-input
      [formControl]="userControl"
      [data]="users"
      label="Select User"
      placeholder="Search users by name or email..."
      displayField="label">
    </app-filter-selection-input>
    
    <div *ngIf="userControl.value">
      Selected User: {{ userControl.value.label }} ({{ userControl.value.email }})
    </div>
  `
})
export class ObjectFilterComponent {
  userControl = new FormControl();
  
  users = [
    { id: 1, label: 'John Doe', email: '[email protected]', department: 'Engineering' },
    { id: 2, label: 'Jane Smith', email: '[email protected]', department: 'Marketing' },
    { id: 3, label: 'Bob Johnson', email: '[email protected]', department: 'Sales' },
    { id: 4, label: 'Alice Brown', email: '[email protected]', department: 'HR' },
    { id: 5, label: 'Charlie Wilson', email: '[email protected]', department: 'Engineering' },
    { id: 6, label: 'Diana Davis', email: '[email protected]', department: 'Finance' },
    { id: 7, label: 'Edward Miller', email: '[email protected]', department: 'Operations' },
    { id: 8, label: 'Fiona Garcia', email: '[email protected]', department: 'Marketing' }
  ];
}

Example 3: Required Selection with Validation

import { Component } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-required-filter',
  template: `
    <app-filter-selection-input
      [formControl]="categoryControl"
      [data]="categories"
      label="Product Category"
      placeholder="Search categories..."
      [required]="true">
    </app-filter-selection-input>
    
    <div class="errors" *ngIf="categoryControl.errors && categoryControl.touched">
      <div *ngIf="categoryControl.hasError('required')">
        Category selection is required
      </div>
    </div>
    
    <div>Selected Category: {{ categoryControl.value }}</div>
  `
})
export class RequiredFilterComponent {
  categoryControl = new FormControl('', Validators.required);
  
  categories = [
    'Electronics', 'Clothing', 'Books', 'Home & Garden', 'Sports & Outdoors',
    'Automotive', 'Health & Beauty', 'Toys & Games', 'Food & Beverages',
    'Office Supplies', 'Art & Collectibles', 'Musical Instruments',
    'Pet Supplies', 'Baby Products', 'Tools & Hardware'
  ];
}

Component API

Inputs

| Input | Type | Description | Default | | :--- | :--- | :--- | :--- | | data | any[] \| string[] | Array of options to filter and select from | (Required) | | label | string | Label text for the input field | undefined | | placeholder | string | Placeholder text for the input field | 'Start typing to search...' | | displayField | string | Property name to display for object arrays | undefined | | required | boolean | Whether selection is required | false | | disabled | boolean | Whether the input is disabled | false |

Outputs

| Output | Type | Description | |--------|------|-------------| | filterChange | EventEmitter<string> | Emits when filter text changes | | selectionChange | EventEmitter<any> | Emits 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 value
writeValue(value: any): 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;
}

Module Configuration

FilterSelectionInputModule

No Global Configuration Required

The FilterSelectionInputModule 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
  • @angular/material: Material Design components

Advanced Usage Patterns

Custom Filtering Logic

import { Component } from '@angular/core';

@Component({
  selector: 'app-custom-filtering',
  template: `
    <app-filter-selection-input
      [formControl]="customControl"
      [data]="complexData"
      label="Search Products"
      placeholder="Search by name, category, or SKU..."
      displayField="displayName">
    </app-filter-selection-input>
  `
})
export class CustomFilteringComponent {
  customControl = new FormControl();
  
  complexData = [
    { 
      id: 1, 
      name: 'iPhone 15 Pro', 
      category: 'Electronics', 
      sku: 'IPH15P-256-BLU',
      displayName: 'iPhone 15 Pro 256GB Blue'
    },
    { 
      id: 2, 
      name: 'MacBook Air M2', 
      category: 'Computers', 
      sku: 'MBA-M2-512-SLV',
      displayName: 'MacBook Air M2 512GB Silver'
    }
  ];
}

Async Data Loading

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-async-filter',
  template: `
    <app-filter-selection-input
      [formControl]="asyncControl"
      [data]="asyncOptions"
      label="Search Users"
      placeholder="Search users by name or email..."
      displayField="displayName"
      [loading]="loading"
      (filterChange)="onFilterChange($event)">
    </app-filter-selection-input>
    
    <div *ngIf="loading" class="loading-indicator">
      Loading options...
    </div>
  `
})
export class AsyncFilterComponent {
  asyncControl = new FormControl();
  asyncOptions: any[] = [];
  loading = false;
  debounceTimer: any;

  constructor(private http: HttpClient) {}

  onFilterChange(filterText: string) {
    clearTimeout(this.debounceTimer);
    
    this.debounceTimer = setTimeout(() => {
      this.loadOptions(filterText);
    }, 300);
  }

  private loadOptions(filterText: string) {
    this.loading = true;
    
    this.http.get<any[]>(`/api/users/search?q=${encodeURIComponent(filterText)}`)
      .subscribe({
        next: (options) => {
          this.asyncOptions = options;
          this.loading = false;
        },
        error: (error) => {
          console.error('Error loading options:', error);
          this.loading = false;
        }
      });
  }
}

Integration Examples

With Other UI Components

import { Component } from '@angular/core';

@Component({
  selector: 'app-integrated-filter',
  template: `
    <app-display-card title="User Selection">
      <app-filter-selection-input
        [formControl]="userControl"
        [data]="users"
        label="Select User"
        placeholder="Search users..."
        displayField="name">
      </app-filter-selection-input>
      
      <div *ngIf="userControl.value" class="user-details">
        <h4>{{ userControl.value.name }}</h4>
        <p>{{ userControl.value.email }}</p>
        <p>Department: {{ userControl.value.department }}</p>
      </div>
    </app-display-card>
  `
})
export class IntegratedFilterComponent {
  userControl = new FormControl();
  
  users = [
    { id: 1, name: 'John Doe', email: '[email protected]', department: 'Engineering' },
    { id: 2, name: 'Jane Smith', email: '[email protected]', department: 'Marketing' }
  ];
}

Testing

Unit Testing Example

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FilterSelectionInputComponent } from './filter-selection-input.component';
import { ReactiveFormsModule } from '@angular/forms';

describe('FilterSelectionInputComponent', () => {
  let component: FilterSelectionInputComponent;
  let fixture: ComponentFixture<FilterSelectionInputComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ FilterSelectionInputComponent ],
      imports: [ ReactiveFormsModule ]
    }).compileComponents();

    fixture = TestBed.createComponent(FilterSelectionInputComponent);
    component = fixture.componentInstance;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should filter data based on input', () => {
    component.data = ['Apple', 'Banana', 'Cherry', 'Date'];
    component.filterText = 'Ban';
    fixture.detectChanges();
    
    expect(component.filteredData).toEqual(['Banana']);
  });

  it('should emit selection changes', () => {
    spyOn(component.selectionChange, 'emit');
    
    component.onSelectionChange('Apple');
    
    expect(component.selectionChange.emit).toHaveBeenCalledWith('Apple');
  });
});

Troubleshooting

Common Issues

  1. Filtering not working: Ensure data array is properly formatted
  2. Selection not updating: Check form control integration
  3. Object display issues: Set displayField property for object arrays
  4. Styling issues: Verify Material theme is properly configured

Debug Mode

@Component({
  template: `
    <div class="debug-info">
      Filter Text: {{ filterText }}<br>
      Filtered Data Length: {{ filteredData?.length || 0 }}<br>
      Selected Value: {{ formControl?.value | json }}<br>
      Form Control Valid: {{ formControl?.valid }}
    </div>
    
    <app-filter-selection-input
      [formControl]="formControl"
      [data]="data"
      (filterChange)="onFilterChange($event)"
      (selectionChange)="onSelectionChange($event)">
    </app-filter-selection-input>
  `
})
export class DebugFilterComponent {
  formControl = new FormControl();
  data: any[] = [];
  filterText = '';

  onFilterChange(filter: string) {
    this.filterText = filter;
    console.log('Filter changed:', filter);
  }

  onSelectionChange(selection: any) {
    console.log('Selection changed:', selection);
  }
}