How to use debounceTime in an angular component?
Asked Answered
M

2

26

My requirement is to perform reactive form field validations in such a way that the error messages are displayed only after the user stops typing.

How can I accomplish this using reactive forms and Rxjs debounceTime?

I'm looking for a solution that works with Reactive forms

Meridel answered 7/6, 2018 at 10:42 Comment(3)
https://mcmap.net/q/49644/-angular-2-and-debounce]Dairen
Possible duplicate of Angular and debounceDairen
Hi Ric, the above answer does not show how to use debounce in reactive form validationMeridel
S
37

The (or at least a) way to get this to work is to dynamically remove and add your validators as you go.

On your input(s), use a keydown binding that will strip away validators when the user starts to type, and a keyup binding that will run through a debounceTime pipe and then reapply the validators (but only after the specified debounce time has passed).

Code here:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';

@Component({
    selector: 'form-component',
    template: `
        <form [formGroup]="formGroup">
          <input type="text" formControlName="name" (keyup)="onKeyUp()" (keydown)="onKeyDown()" [ngClass]="{ 'invalid': formGroup.controls.name.invalid }">
        </form>
      `,
    styles: [
        '.invalid { border-color: red; color: red; }'
    ]
})
export class FormComponent implements OnInit {

    formGroup: FormGroup;
    subject: Subject<any> = new Subject();

    constructor(private formBuilder: FormBuilder) {}

    ngOnInit(): void {
        this.formGroup = this.formBuilder.group({
            name: [ '' ]
        });

        // Subscribe to the subject, which is triggered with each keyup
        // When the debounce time has passed, we add a validator and update the form control to check validity
        this.subject
            .pipe(debounceTime(500))
            .subscribe(() => {
                    this.formGroup.controls.name.setValidators([ Validators.minLength(5) ]);
                    this.formGroup.controls.name.updateValueAndValidity();
                }
            );
    }

    onKeyUp(): void {
        this.subject.next();
    }

    onKeyDown(): void {
        // When the user starts to type, remove the validator
        this.formGroup.controls.name.clearValidators();
    }

}

And StackBlitz here: https://stackblitz.com/edit/debounce-validator

Shepperd answered 7/6, 2018 at 11:50 Comment(0)
K
7

debounceTime waits for the time period mentioned and then calls the subscribe method. e.g; debounceTime(1000) will wait for 1 second. It is implemented through pipes.

this can be added to any subscribe method. Following is the working example

import { Component, OnInit } from '@angular/core';
import { Validators, AbstractControl } from '@angular/forms';
import { debounceTime } from 'rxjs/operators';

// dynamic forms
import { FormGroup, FormControl, FormBuilder } from '@angular/forms';


@Component({
    selector: 'app-customer-form',
    templateUrl: './customer-form.component.html',
})
export class CustomerFormComponent implements OnInit {

    emailMessage : string;

    private validationMessages = {
        required: "Email field is required",
        email: "Please enter a valid Email"
    }

    customerForm: FormGroup;

    customer = new Customer();

    constructor(private fb: FormBuilder) { }

    ngOnInit() {
        this.customerForm = this.fb.group({
            emailAddress: ['',
                [
                    Validators.required,
                    Validators.email
                ]
            ]
        })

        const emailControl = this.customerForm.get('emailAddress');
        emailControl.valueChanges.pipe( debounceTime(1000) ).subscribe(
            value => this.setEmailMessage(emailControl)
        )
    }

    setEmailMessage( c: AbstractControl ) : void {
        this.emailMessage = '';

        if ( (c.touched || c.dirty) && c.errors ) {
            this.emailMessage = Object.keys(c.errors).map( key => this.validationMessages[key]).join(' ');
        }

    }

}

in your template

<input 
    class="form-control"
    id="emailId" type="email"
    placeholder="Email (required)"
    formControlName="emailAddress"
    [ngClass]="{ 'is-invalid': emailMessage }"/>
    <span class="invalid-feedback">
    {{ emailMessage }}
    </span>
Keene answered 21/11, 2019 at 6:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.