I am wondering how can I implement the debounce time on my async validator.
I have the following:
...
password: ['',Validators.compose([Validators.required, this.passwordValid])]
...
Where:
passwordValid(control:Control):{ [key: string]: any; } {
return new Promise(resolve => {
this._http.post('/passwordCheck', control.value)
.subscribe(
success=>{
resolve(null);
},
error=>{
resolve({passwordValid: false})
}
)
})
}
However now, the validation is triggered at each keystroke. I need to add debounce functionality. How can I do that?
It's not possible out of the box since the validator is directly triggered when the input
event is used to trigger updates. See this line in the source code:
If you want to leverage a debounce time at this level, you need to get an observable directly linked with the input
event of the corresponding DOM element. This issue in Github could give you the context:
In your case, a workaround would be to implement a custom value accessor leveraging the fromEvent
method of observable.
Here is a sample:
const DEBOUNCE_INPUT_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DebounceInputControlValueAccessor), multi: true});
@Directive({
selector: '[debounceTime]',
//host: {'(change)': 'doOnChange($event.target)', '(blur)': 'onTouched()'},
providers: [DEBOUNCE_INPUT_VALUE_ACCESSOR]
})
export class DebounceInputControlValueAccessor implements ControlValueAccessor {
onChange = (_) => {};
onTouched = () => {};
@Input()
debounceTime:number;
constructor(private _elementRef: ElementRef, private _renderer:Renderer) {
}
ngAfterViewInit() {
Observable.fromEvent(this._elementRef.nativeElement, 'keyup')
.debounceTime(this.debounceTime)
.subscribe((event) => {
this.onChange(event.target.value);
});
}
writeValue(value: any): void {
var normalizedValue = isBlank(value) ? '' : value;
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
}
registerOnChange(fn: () => any): void { this.onChange = fn; }
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
}
And use it this way:
function validator(ctrl) {
console.log('validator called');
console.log(ctrl);
}
@Component({
selector: 'app'
template: `
<form>
<div>
<input [debounceTime]="2000" [ngFormControl]="ctrl"/>
</div>
value : {{ctrl.value}}
</form>
`,
directives: [ DebounceInputControlValueAccessor ]
})
export class App {
constructor(private fb:FormBuilder) {
this.ctrl = new Control('', validator);
}
}
See this plunkr: https://plnkr.co/edit/u23ZgaXjAvzFpeScZbpJ?p=preview.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With