import { AfterViewInit, Component, ElementRef, EventEmitter, Input, NgZone, Output, ViewChild } from '@angular/core'; import { FormControl } from '@angular/forms'; import { debounceTime, fromEvent, switchMap } from 'rxjs'; import { Place } from './interface'; import { MapPin, Search, CircleAlert } from 'lucide-angular'; @Component({ selector: 'kit-autocomplete-google-map', templateUrl: './autocomplete-google-map.component.html', styleUrls: ['../styles/index.scss'] }) export class AutocompleteGoogleMapComponent implements AfterViewInit { readonly searchI = Search readonly mappin = MapPin readonly infoCircle = CircleAlert; @Input() placeholderText: string = ""; @Input() label: string = ""; @Input() control: any; @Input() minimumCharacterLength: number = 1; private sessionToken: any; private autocompleteService: any; public predictionList: Array = []; public options: google.maps.places.AutocompletePrediction[] = []; @ViewChild('search') searchElementRef!: ElementRef; @Output() changeSelectedLocation = new EventEmitter(); @Output() changeSelectedLocationControl = new EventEmitter>(); @Input() addressValidationMessage: string = '' @Input() descriptionTooltip: string = ''; @Input() countryCode: string = ''; constructor(private ngZone: NgZone) { } ngAfterViewInit(): void { this.sessionToken = new google.maps.places.AutocompleteSessionToken(); this.autocompleteService = new google.maps.places.AutocompleteService(); this.setAutoCompleteMaps() } private _filter(value: any): google.maps.places.AutocompletePrediction[] | any { if (!value) { return []; } else { return this.options; } } /** * * @param placePrediction * 1) The details of the address selected from the list are consulted using the PlacesService getDetails by sending * as a parameter the place_id of the selected address and the previously generated sessionToken. * 2) If you do not obtain results, clean the input. * 3) Otherwise, the address details are obtained as a response with values such as the address_component that * includes the street number and among others that are necessary to map them in the contact object. */ public selectedDirection($event: any) { const placeSelected = $event.option.value; this.control.setValue(placeSelected.description); this.ngZone.run(() => { const placesService = new google.maps.places.PlacesService(this.searchElementRef.nativeElement); placesService.getDetails({ placeId: placeSelected.place_id, sessionToken: this.sessionToken }, (place: any, status: string) => { if (status === google.maps.places.PlacesServiceStatus.OK) { if (place && (place.geometry === undefined || place.geometry === null)) { this.control.setValue(null) return; } else { this.changeSelectedLocation.emit(place); this.changeSelectedLocationControl.emit(this.control); this.control.setValue(place && place.formatted_address ? place.formatted_address : ""); this.options = []; } } }); }); } public setAutoCompleteMaps() { const getPlacePredictions = (options: any) => { return new Promise((resolve) => { if (options && options.input.length >= this.minimumCharacterLength) { this.changeSelectedLocationControl.emit(this.control) this.autocompleteService.getPlacePredictions(options, (predictions:any, status:any) => { if (status != google.maps.places.PlacesServiceStatus.OK) { resolve([]); } else { resolve(predictions); } }); } else if (options && options.input.length <= this.minimumCharacterLength) { this.changeSelectedLocationControl.emit(this.control) } else if (options && options.input === ''){ this.control.setValue(""); this.options = []; this.changeSelectedLocationControl.emit(this.control); } }); }; const inputObservable = fromEvent(this.searchElementRef.nativeElement, 'input').pipe( debounceTime(700), // Retrasa la ejecución hasta que el usuario haya dejado de escribir durante 700 ms switchMap(() => { const request: any = { input: this.searchElementRef.nativeElement.value, sessionToken: this.sessionToken, types: ['address'], }; if (this.countryCode) { request.componentRestrictions = { country: this.countryCode }; } return getPlacePredictions(request); }) ); inputObservable.subscribe((predictions: any) => { this.predictionList = predictions; }); } isControlRequired(): boolean { if (this.control && this.control.validator) { const validator = this.control.validator({} as any); return !!(validator && validator.required); } return false } }