Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use debounceTime in an angular component?

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

like image 439
Rohit Avatar asked Jun 07 '18 10:06

Rohit


People also ask

How do you use debounceTime?

The Debouncetime operator starts counting time after it receives a value. If the source observable emits a value before the timeout duration, then counting is reset to zero & started again. When the timeout duration elapses the operator emits the last value and the counting stops.

How do I use debounceTime in angular 6?

Angular 6 onwards, debounceTime is imported as following. import { debounceTime } from 'rxjs/operators'; It is used with pipe operator of Observable . debounceTime is useful in operation where user changes inputs frequently such as search operation.

What is debounceTime RXJS?

debounceTime delays the values emitted by a source for the given due time. If within this time a new value arrives, the previous pending value is dropped and the timer is reset. In this way debounceTime keeps track of most recent value and emits that most recent value when the given due time is passed.

What is Debouncing in angular?

Debouncing is the delay of a function/method execution or an action for a period of the specified time. During this specified time, calls to the method/function or action are collected and executes each one when the specified has elapsed.


2 Answers

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

like image 63
lordchancellor Avatar answered Sep 20 '22 10:09

lordchancellor


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>
like image 38
Aamer Shahzad Avatar answered Sep 18 '22 10:09

Aamer Shahzad