Base solution for your next web application
Open Closed

Unable to get Address Autocomplete powered by google working #7036


User avatar
0
abrewer created

I have the Angular UI project, and I am attempting to create an address autoComplete component for my app. I am unable to get this working.

i have created a component called locationAutoComplete.component.ts

import { Component, ViewChild, EventEmitter, Output, OnInit, AfterViewInit, Input } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { appModuleAnimation } from '@shared/animations/routerTransition';
/// <reference types=”@types/googlemaps” />

@Component({
    selector: 'LocationAutocompleteComponent',
    templateUrl: './locationAutoComplete.component.html',
    animations: [appModuleAnimation()]
})
export class LocationAutocompleteComponent implements OnInit, AfterViewInit {
    @Input() addressType: string;
    @Output() setAddress: EventEmitter<any> = new EventEmitter();
    @ViewChild('addresstext') addresstext: any;

    locationAutocompleteInput: string;
    queryWait: boolean;

    constructor() {
    }

    ngOnInit() {
    }

    ngAfterViewInit() {
        this.getPlaceAutocomplete();
    }

    private getPlaceAutocomplete() {
        const autocomplete = new google.maps.places.Autocomplete(this.addresstext.nativeElement,
            {
                componentRestrictions: { country: 'US' },
                types: [this.addressType]  // 'establishment' / 'address' / 'geocode'
            });

        google.maps.event.addListener(autocomplete, 'place_changed', () => {
            const place = autocomplete.getPlace();
            this.invokeEvent(place);

        });
    }

    invokeEvent(place: Object) {
        this.setAddress.emit(place);
    }

}
  1. Added a script reference to my Index.html
<!doctype html>
<html lang="en" dir="ltr">
<head prefix="og: http://ogp.me/ns#">
    <meta charset="utf-8">
    <title>Orca</title>
    <base href="/">

    <meta property="og:title" content="Orca" />
    <meta property="og:image" content="" />
    <meta property="og:description" content="Base solution for your next web application" />
    <meta property="og:url" content="">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="author" content="">
    <meta name="description" content="">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
    <app-root></app-root>
    <script src="https://maps.googleapis.com/maps/api/js?key=[API_KEY]&libraries=places"></script>
</body>
</html>
  1. Included the locationAutoComplete.component.ts in a modal editing screen.
<div bsModal #createOrEditModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="createOrEditModal" aria-hidden="true" [config]="{backdrop: 'static'}">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <form *ngIf="active" #locationForm="ngForm" novalidate (ngSubmit)="save()" autocomplete="off">
                <div class="modal-header">
                    <h4 class="modal-title">
                        <span *ngIf="location.id">{{l("EditLocation")}}</span>
                        <span *ngIf="!location.id">{{l("CreateNewLocation")}}</span>
                    </h4>
                    <button type="button" class="close" (click)="close()" aria-label="Close" [disabled]="saving">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="form-group m-form__group">
                        <label for="LocationGroupName">{{l("LocationGroup")}}</label>
                        <div class="input-group">
                            <input class="form-control" id="LocationGroupName" name="locationGroupName" [(ngModel)]="locationGroupName" type="text" disabled>
                            <div class="input-group-append">
                                <button class="btn btn-primary blue" (click)="openSelectLocationGroupModal()" type="button"><i class="fa fa-search"></i> {{l("Pick")}}</button>
                            </div> <div class="input-group-prepend">
                                <button class="btn btn-danger" type="button" (click)="setLocationGroupIdNull()"><i class="fa fa-times"></i></button>
                            </div>
                        </div>
                    </div>
                    <input class="form-control" name="location.locationGroupId" [(ngModel)]="location.locationGroupId" type="text" hidden>


                    <div class="form-group">
                        <label for="Location_Name">{{l("Name")}} *</label>
                        <input type="text" id="Location_Name" class="form-control" [(ngModel)]="location.name" name="Name" minlength="1" maxlength="50" required />
                    </div>

                    <div class="form-group">
                        <label for="Location_Description">{{l("Description")}}</label>
                        <input type="text" id="Location_Description" class="form-control" [(ngModel)]="location.description" name="Description" minlength="0" maxlength="280" />
                    </div>

                    <div class="form-group">
                        <label for="Location_Lookup">{{l("LocationLookup")}}</label>
                        <LocationAutocompleteComponent (setAddress)="getEstablishmentAddress($event)" addressType="establishment"></LocationAutocompleteComponent>
                    </div>

                    <div class="form-group">
                        <label for="Location_StreetAddress">{{l("StreetAddress")}} *</label>
                        <input type="text" id="Location_StreetAddress" class="form-control" [(ngModel)]="location.streetAddress" name="StreetAddress" minlength="1" maxlength="100" required />
                    </div>

                    <div class="form-group">
                        <label for="Location_City">{{l("City")}} *</label>
                        <input type="text" id="Location_City" class="form-control" [(ngModel)]="location.city" name="City" minlength="1" maxlength="100" required />
                    </div>

                    <div class="form-group">
                        <label for="Location_StateProv">{{l("StateProv")}} *</label>
                        <input type="text" id="Location_StateProv" class="form-control" [(ngModel)]="location.stateProv" name="StateProv" minlength="1" maxlength="100" required />
                    </div>

                    <div class="form-group">
                        <label for="Location_Country">{{l("Country")}} *</label>
                        <input type="text" id="Location_Country" class="form-control" [(ngModel)]="location.country" name="Country" minlength="1" maxlength="100" required />
                    </div>

                    <div class="form-group">
                        <label for="Location_PostalCode">{{l("PostalCode")}}</label>
                        <input type="text" id="Location_PostalCode" class="form-control" [(ngModel)]="location.postalCode" name="PostalCode" minlength="1" maxlength="100" />
                    </div>

                    <div class="form-group">
                        <label for="Location_Latitude">{{l("Latitude")}}</label>
                        <input type="number" id="Location_Latitude" class="form-control" [(ngModel)]="location.latitude" name="Latitude" />
                    </div>

                    <div class="form-group">
                        <label for="Location_Longitude">{{l("Longitude")}}</label>
                        <input type="number" id="Location_Longitude" class="form-control" [(ngModel)]="location.longitude" name="Longitude" />
                    </div>


                </div>
                <div class="modal-footer">
                    <button [disabled]="saving" type="button" class="btn btn-default" (click)="close()">{{l("Cancel")}}</button>
                    <button type="submit" class="btn btn-primary blue" [disabled]="!locationForm.form.valid" [buttonBusy]="saving" [busyText]="l('SavingWithThreeDot')"><i class="fa fa-save"></i> <span>{{l("Save")}}</span></button>
                </div>
            </form>
        </div>
    </div>
<locationGroupLookupTableModal #locationGroupLookupTableModal (modalSave)="getNewLocationGroupId()"></locationGroupLookupTableModal>

</div>

I am seeing calls to my Google API based on the traffic on the google console, but I am not getting the autoComplete experience on the UI.

Interestingly, when I start to type an address, nothing happens. But when I hit enter after typing the address. i see the following in chrome dev tools

<br>

CreateOrEditLocationModalComponent.html:41 ERROR TypeError: _co.getEstablishmentAddress is not a function
    at Object.eval [as handleEvent] (CreateOrEditLocationModalComponent.html:41)
    at handleEvent (core.js:23107)
    at callWithDebugContext (core.js:24177)
    at Object.debugHandleEvent [as handleEvent] (core.js:23904)
    at dispatchEvent (core.js:20556)
    at core.js:22046
    at SafeSubscriber.schedulerFn [as _next] (core.js:13527)
    at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:194)
    at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:132)
    at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next (Subscriber.js:76)
View_CreateOrEditLocationModalComponent_1 @ CreateOrEditLocationModalComponent.html:41
push../node_modules/@angular/core/fesm5/core.js.DebugContext_.logError @ core.js:24139
push../node_modules/@angular/core/fesm5/core.js.ErrorHandler.handleError @ core.js:15772
dispatchEvent @ core.js:20560
(anonymous) @ core.js:22046
schedulerFn @ core.js:13527
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub @ Subscriber.js:194
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next @ Subscriber.js:132
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next @ Subscriber.js:76
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:53
push../node_modules/rxjs/_esm5/internal/Subject.js.Subject.next @ Subject.js:47
push../node_modules/@angular/core/fesm5/core.js.EventEmitter.emit @ core.js:13499
push../src/app/shared/common/locationUtilities/locationAutoComplete.component.ts.LocationAutocompleteComponent.invokeEvent @ locationAutoComplete.component.ts:44
(anonymous) @ locationAutoComplete.component.ts:38
Nd.A @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:170
_.R.trigger @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:167
Rd @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:70
Rd @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:70
_.S.set @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:170
(anonymous) @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:72
_.p.Vl @ places_impl.js:26
Nd.A @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:170
_.R.trigger @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:167
(anonymous) @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:69
Nd.A @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:170
_.R.trigger @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:167
b4 @ places_impl.js:15
_.p.Al @ places_impl.js:30
(anonymous) @ js?key=AIzaSyBKIkofPok4_DYZBy6oZt9Sq2wuDP-CpQk&libraries=places:69
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:423
onInvokeTask @ core.js:17290
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:422
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:195
push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:498
invokeTask @ zone.js:1693
globalZoneAwareCallback @ zone.js:1719
Show 6 more frames
CreateOrEditLocationModalComponent.html:41 ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 66, nodeDef: {…}, elDef: {…}, elView: {…}}

5 Answer(s)
  • User Avatar
    0
    aaron created
    Support Team

    The error message is pretty straightforward. Implement getEstablishmentAddress.

  • User Avatar
    0
    abrewer created

    I have implemented this within the modal edit component's TS, but i am still not getting the desired behavior. I am not getting the AutoComplete dropdown window to appear as soon as you begin typing.

    import { Component, ViewChild, Injector, Output, EventEmitter, NgZone} from '@angular/core';
    import { ModalDirective } from 'ngx-bootstrap';
    import { finalize } from 'rxjs/operators';
    import { LocationsServiceProxy, CreateOrEditLocationDto } from '@shared/service-proxies/service-proxies';
    import { AppComponentBase } from '@shared/common/app-component-base';
    import * as moment from 'moment';
    import { LocationGroupLookupTableModalComponent } from './locationGroup-lookup-table-modal.component';
    
    @Component({
        selector: 'createOrEditLocationModal',
        templateUrl: './create-or-edit-location-modal.component.html'
    })
    
    export class CreateOrEditLocationModalComponent extends AppComponentBase {
    
        @ViewChild('createOrEditModal') modal: ModalDirective;
        @ViewChild('locationGroupLookupTableModal') locationGroupLookupTableModal: LocationGroupLookupTableModalComponent;
    
    
        @Output() modalSave: EventEmitter<any> = new EventEmitter<any>();
    
        active = false;
        saving = false;
    
        location: CreateOrEditLocationDto = new CreateOrEditLocationDto();
    
        locationGroupName = '';
    
        address: Object;
        establishmentAddress: Object;
    
        formattedAddress: string;
        formattedEstablishmentAddress: string;
    
        phone: string;
    
        constructor(injector: Injector, private _locationsServiceProxy: LocationsServiceProxy, public zone: NgZone) {
            super(injector);
        }
    
        show(locationId?: number): void {
    
            if (!locationId) {
                this.location = new CreateOrEditLocationDto();
                this.location.id = locationId;
                this.locationGroupName = '';
    
                this.active = true;
                this.modal.show();
            } else {
                this._locationsServiceProxy.getLocationForEdit(locationId).subscribe(result => {
                    this.location = result.location;
    
                    this.locationGroupName = result.locationGroupName;
    
                    this.active = true;
                    this.modal.show();
                });
            }
        }
    
        save(): void {
                this.saving = true;
    
    
                this._locationsServiceProxy.createOrEdit(this.location)
                 .pipe(finalize(() => { this.saving = false;}))
                 .subscribe(() => {
                    this.notify.info(this.l('SavedSuccessfully'));
                    this.close();
                    this.modalSave.emit(null);
                 });
        }
    
            openSelectLocationGroupModal() {
            this.locationGroupLookupTableModal.id = this.location.locationGroupId;
            this.locationGroupLookupTableModal.displayName = this.locationGroupName;
            this.locationGroupLookupTableModal.show();
        }
    
    
            setLocationGroupIdNull() {
            this.location.locationGroupId = null;
            this.locationGroupName = '';
        }
    
    
            getNewLocationGroupId() {
            this.location.locationGroupId = this.locationGroupLookupTableModal.id;
            this.locationGroupName = this.locationGroupLookupTableModal.displayName;
        }
    
    
        close(): void {
    
            this.active = false;
            this.modal.hide();
        }
    
        getAddress(place: object) {
            this.address = place['formatted_address'];
            this.phone = this.getPhone(place);
            this.formattedAddress = place['formatted_address'];
            this.zone.run(() => this.formattedAddress = place['formatted_address']);
        }
    
        getEstablishmentAddress(place: object) {
            this.establishmentAddress = place['formatted_address'];
            this.phone = this.getPhone(place);
            this.formattedEstablishmentAddress = place['formatted_address'];
            this.zone.run(() => {
                this.formattedEstablishmentAddress = place['formatted_address'];
                this.phone = place['formatted_phone_number'];
            });
        }
    
        getAddrComponent(place, componentTemplate) {
            let result;
    
            for (let i = 0; i < place.address_components.length; i++) {
                const addressType = place.address_components[i].types[0];
                if (componentTemplate[addressType]) {
                    result = place.address_components[i][componentTemplate[addressType]];
                    return result;
                }
            }
            return;
        }
    
        getStreetNumber(place) {
            const COMPONENT_TEMPLATE = { street_number: 'short_name' },
                streetNumber = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return streetNumber;
        }
    
        getStreet(place) {
            const COMPONENT_TEMPLATE = { route: 'long_name' },
                street = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return street;
        }
    
        getCity(place) {
            const COMPONENT_TEMPLATE = { locality: 'long_name' },
                city = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return city;
        }
    
        getState(place) {
            const COMPONENT_TEMPLATE = { administrative_area_level_1: 'short_name' },
                state = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return state;
        }
    
        getDistrict(place) {
            const COMPONENT_TEMPLATE = { administrative_area_level_2: 'short_name' },
                state = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return state;
        }
    
        getCountryShort(place) {
            const COMPONENT_TEMPLATE = { country: 'short_name' },
                countryShort = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return countryShort;
        }
    
        getCountry(place) {
            const COMPONENT_TEMPLATE = { country: 'long_name' },
                country = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return country;
        }
    
        getPostCode(place) {
            const COMPONENT_TEMPLATE = { postal_code: 'long_name' },
                postCode = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return postCode;
        }
    
        getPhone(place) {
            const COMPONENT_TEMPLATE = { formatted_phone_number: 'formatted_phone_number' },
                phone = this.getAddrComponent(place, COMPONENT_TEMPLATE);
            return phone;
        }
    }
    
  • User Avatar
    0
    abrewer created

    Here is an example of what I am trying to achieve, and I am not getting the dropdown to appear upon my inital typing.

    FYI, here is the code that I am adapting to my project. https://medium.com/@dhormale/use-google-places-api-autocomplete-using-angular-for-resident-and-office-address-23cc33078e8

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @abrewer

    Does that work for you if you do it in a raw Angular project ?

  • User Avatar
    0
    abrewer created

    Thanks for the reply, but I found the problem!

    I was using my autoComplete component within a modal, and the autoComplete dropdown panel need to be "raised" higher than the modal.

    Simply adding

    .pac-container {
        z-index: 10000 !important;
    }
    

    to my CSS caused this to work.