paras shah
paras shah

Reputation: 889

how to set debounce time to call for autocomplete google map api call from angular6

I have made a custom directive to use google api autocomplete functionality and trying to reduce number of api call to google for here is my code.

Please suggest me something to set kind of delay for less api call to google server for input auto complete functionality

this code calls api per letter input i want to make it after 4 to 5 second or after typing of a word

//Google-place-directive.ts

import {
  Directive,
  ElementRef,
  OnInit,
  Output,
  EventEmitter
} from "@angular/core";

declare var google: any;

@Directive({
  selector: "[google-place]"
})
export class GooglePlacesDirective implements OnInit {
  @Output() onSelect: EventEmitter<any> = new EventEmitter();
  private element: HTMLInputElement;

  constructor(elRef: ElementRef) {
    //elRef will get a reference to the element where
    //the directive is placed
    this.element = elRef.nativeElement;
  }

  getFormattedAddress(place) {
    //@params: place - Google Autocomplete place object
    //@returns: location_obj - An address object in human readable format
    let location_obj = {};
    console.log(place);
    console.log(place.geometry.location.lat());
    console.log(place.geometry.location.lng());
    for (let i in place.address_components) {
      let item = place.address_components[i];

      location_obj["formatted_address"] = place.formatted_address;
      if (item["types"].indexOf("locality") > -1) {
        location_obj["locality"] = item["long_name"];
      } else if (item["types"].indexOf("administrative_area_level_1") > -1) {
        location_obj["admin_area_l1"] = item["short_name"];
      } else if (item["types"].indexOf("street_number") > -1) {
        location_obj["street_number"] = item["short_name"];
      } else if (item["types"].indexOf("route") > -1) {
        location_obj["route"] = item["long_name"];
      } else if (item["types"].indexOf("country") > -1) {
        location_obj["country"] = item["long_name"];
      } else if (item["types"].indexOf("postal_code") > -1) {
        location_obj["postal_code"] = item["short_name"];
      }
    }
    return location_obj;
  }

  ngOnInit() {
    const autocomplete = new google.maps.places.Autocomplete(this.element);
    //Event listener to monitor place changes in the input
    google.maps.event.addListener(autocomplete, "place_changed", () => {
      //Emit the new address object for the updated place
      this.onSelect.emit(this.getFormattedAddress(autocomplete.getPlace()));
    });
  }
}

//html is like

 <input
                type="text"
                class="google-place-input"
                google-place
                (onSelect)="setAddress($event)"
                placeholder="Type to search.."
              />

Thanks in advance

Upvotes: 0

Views: 3587

Answers (1)

Vladimir Prudnikov
Vladimir Prudnikov

Reputation: 7242

Create an Observable from the event and then apply debounceTime operator

import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';

//create observable that emits 'place_changed' events
const source = fromEvent(autocomplete, 'place_changed');

//map to string with given event timestamp
const example = source.pipe(debounceTime(4000));

const subscribe = example.subscribe(val => console.log(val));

By the way, 4-5 sec is too much. Debounce time is the time after last key pressed.

UPDATE #1

Try to add the following directive on your input where user types his query

debounce-time.directive.ts

import {AfterViewInit, Directive, ElementRef, forwardRef, Input, OnDestroy, Renderer2} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {fromEvent, Subject} from 'rxjs';
import {debounceTime, takeUntil} from 'rxjs/operators';

// tslint:disable:directive-selector
@Directive({
  selector: '[debounceTime]',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DebounceTimeDirective),
    multi: true
  }]
})
export class DebounceTimeDirective extends Destroyable implements ControlValueAccessor, AfterViewInit, OnDestroy {
  protected destroyed$ = new Subject<boolean>();

  @Input()
  debounceTime: number;

  onChange = (_) => {};
  onTouched = () => {};

  constructor(private _elementRef: ElementRef, private renderer: Renderer2) {
    super();
  }

  ngAfterViewInit() {
    fromEvent(this._elementRef.nativeElement, 'keyup')
      .pipe(
        takeUntil(this.destroyed$),
        debounceTime(this.debounceTime)
      )
      .subscribe((event: any) => {
        this.onChange(event.target.value);
      });
  }

  ngOnDestroy(): void {
    this.destroyed$.next(true);
    this.destroyed$.complete();
  }

  writeValue(value: any): void {
    const normalizedValue = value === null || value === undefined ? '' : value;
    this.renderer.setProperty(this._elementRef.nativeElement, 'value', normalizedValue);
  }

  registerOnChange(fn: () => any): void { this.onChange = fn; }
  registerOnTouched(fn: () => any): void { this.onTouched = fn; }
}

your-template.html

<input [debounceTime]="4000" ... />

Upvotes: 2

Related Questions