Skip to content

Instantly share code, notes, and snippets.

@LcsGa
Last active August 27, 2025 23:19
Show Gist options
  • Select an option

  • Save LcsGa/9336fc5240840c33eb9093eb8a554535 to your computer and use it in GitHub Desktop.

Select an option

Save LcsGa/9336fc5240840c33eb9093eb8a554535 to your computer and use it in GitHub Desktop.
Debounce emitters, not signals
@Component({
...
template: `<input [(ngxDebounceInput)]="search" />`
})
export class Example {
readonly search = signal('');
}
import {
Directive,
inject,
ElementRef,
input,
numberAttribute,
InjectionToken,
type Provider,
effect,
} from '@angular/core';
import { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
import { isNil } from 'lodash-es';
import { switchMap, fromEvent, debounceTime, map } from 'rxjs';
type Config = {
duration: number;
};
const DEFAULT_CONFIG: Config = {
duration: 300,
};
const DEBOUNCE_INPUT_DEFAULT_CONFIG = new InjectionToken<Config>('DebounceInput default config', {
providedIn: 'root',
factory: () => DEFAULT_CONFIG,
});
export function provideDefaultDebounceInputConfig(config: Partial<Config>): Provider {
return { provide: DEBOUNCE_INPUT_DEFAULT_CONFIG, useValue: { ...DEFAULT_CONFIG, ...config } };
}
@Directive({
selector: 'input[ngxDebounceInput], select[ngxDebounceInput], textarea[ngxDebounceInput]',
})
export class DebounceInput {
private readonly config = inject(DEBOUNCE_INPUT_DEFAULT_CONFIG);
private readonly el = inject(ElementRef).nativeElement as HTMLInputElement;
readonly value = input.required<string | number | boolean | undefined | null>({ alias: 'ngxDebounceInput' });
readonly duration = input(this.config.duration, { alias: 'ngxDebounceInputDuration', transform: numberAttribute });
readonly valueChange = outputFromObservable(
toObservable(this.duration).pipe(
switchMap((duration) =>
fromEvent(this.el, 'input').pipe(
debounceTime(duration),
map(() => this.el.value),
),
),
),
{ alias: 'ngxDebounceInputChange' },
);
private readonly updateInputValue = effect(() => {
const value = this.value();
this.el.value = isNil(value) ? '' : String(value);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment