Reputation: 889
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
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.
Try to add the following directive on your input where user types his query
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; }
}
<input [debounceTime]="4000" ... />
Upvotes: 2