Debounce NgModel Form Directive
Monday, November 9, 2020
Debounce NgModel Form Directive
If you are using Angular's template-driven forms you are likely very familiar with the NgModel
directive that creates a FormControl
instance for a form element. In some cases you may want to debounce the value before executing statements as a result of the value change event. This can be pretty easy to do when working with Angular's reactive forms API but can be less intuitive when building forms using the template-driven forms API.
Here is a Directive for debouncing the valueChanges()
observable with a template declarative implementation.
import { Directive, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
import { NgModel } from '@angular/forms';
import { Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, skip } from 'rxjs/operators';
@Directive({
selector: '[ngModelDebounceChange]',
})
export class NgModelDebounceChangeDirective implements OnDestroy, OnInit {
/** Emit event when model has changed. */
@Output() ngModelDebounceChange = new EventEmitter<any>();
/** Subscriptions for cleanup. */
private subscription: Subscription;
ngOnDestroy() {
this.subscription.unsubscribe();
}
constructor(private ngModel: NgModel) {}
ngOnInit(): void {
this.subscription = this.ngModel.control.valueChanges
.pipe(skip(1), debounceTime(200), distinctUntilChanged())
.subscribe(value => this.ngModelDebounceChange.emit(value));
}
}
Let's quickly review the code above.
- First, in the Directive's metadata I've specified the
ngModelDebounceChange
selector. We'll use this selector in place of thengModelChange()
selector that we normally apply to a form element with the template-driven forms API. - Next, we define an Output
EventEmitter
property matching the selector for the Directive. - We track the
Subscription
that is created upon subscribing to the FormControl'svalueChanges
Observable. - The
NgModel
instance is injected into ourconstructor()
function. - Within the
ngOnInit()
lifecycle method we use thedebounceTime()
anddistinctUntilChanged()
operators to debounce the value and to only emit a value that is not equal to the previous value emitted by the Observable. - Finally, we subscribe to the Observable and emit the value.
Instructions
Consider using the ngModelDebounceChange directive to declaratively debounce value changes on a form element
Prevent execution of statements for each value change next notification from a FormControl
Prevent multiple redundant network requests if persisting value changes of a form element
Code Examples
Debounce NgModel value changes in a template-driven form
<form>
<input name="userName" [ngModel]="userName" (ngModelDebounceChange)="onUserNameChange($event)" />
</form>
Have a question or comment?