9

I am building a simple Google Places Autocomplete Angular2 directive but the problem is that cannot get any prediction (the response is always empty?!)...

As a proof of concept I created the simplest possible code snippet as a reference:

<!DOCTYPE html> <html> <head> <script src="https://maps.googleapis.com/maps/api/js?key=[MY_API_KEY]&libraries=places" async defer></script> </head> <body> <button type="button" onclick="go()">go</button> <input id="autocomplete" type="text"></input> <script> var autocomplete = null; function go() { autocomplete = new google.maps.places.Autocomplete( (document.getElementById('autocomplete')), { types: ['geocode'] }); } </script> </body> </html> 

The above code works - I can get predictions after clicking Go button.

Now, Angular2 scenario:

My _Layout.cshtml file has the following tag in head section:

<script src="https://maps.googleapis.com/maps/api/js?key=[MY_API_KEY]&libraries=places" async defer></script> 

My directive:

 import { Directive, ElementRef, Input } from '@angular/core'; declare var google: any; @Directive({ selector: '[googleplaces]' }) export class GooglePlacesDirective { autocomplete: any; constructor(private el: ElementRef) { } ngAfterContentInit() { this.autocomplete = new google.maps.places.Autocomplete( (this.el.nativeElement), { types: ['geocode', 'cities'] }); } } 

And simple Angular component:

<form [formGroup]="companyForm"> . . . <div class="form-group"> <label for="Location">Location</label> <input type="text" class="form-control" id="Location" fromControlName="Location" googleplaces> </div> </form> 

The Scenario 2 (angular) doesn't work. The facts:

  • autocomplete is initialized (it has all expected properties/methods, placeholder is "Enter a location", etc...)
  • autocomplete doesn't return any prediction for typed search string (it returns only "/**/xdc._le3zv3 && xdc._le3zv3( [4] )")

Also, Google API Console says everything is as it should be?! Here is the screenshot:

Dashboard screenshot

What can be in question here? Thanks...

2 Answers 2

14
import {Directive, ElementRef, EventEmitter, Output} from '@angular/core'; import {NgModel} from '@angular/forms'; declare var google:any; @Directive({ selector: '[Googleplace]', providers: [NgModel], host: { '(input)' : 'onInputChange()' } }) export class GoogleplaceDirective { @Output() setAddress: EventEmitter<any> = new EventEmitter(); modelValue:any; autocomplete:any; private _el:HTMLElement; constructor(el: ElementRef,private model:NgModel) { this._el = el.nativeElement; this.modelValue = this.model; var input = this._el; this.autocomplete = new google.maps.places.Autocomplete(input, {}); google.maps.event.addListener(this.autocomplete, 'place_changed', ()=> { var place = this.autocomplete.getPlace(); this.invokeEvent(place); }); } invokeEvent(place:Object) { this.setAddress.emit(place); } onInputChange() { console.log(this.model); } } 

To use

<input type="text" class="form-control" placeholder="Location" name="Location" [(ngModel)]="address" #LocationCtrl="ngModel" Googleplace (setAddress)="getAddressOnChange($event,LocationCtrl)"> 
Sign up to request clarification or add additional context in comments.

6 Comments

Perfect! Thank you very much @Habeeb!
If you get expected result. Please mark as correct answer. meta.stackexchange.com/questions/5234/…
Hi Habeeb, I am new to angular 2.. I am trying to add an autocomplete in my page...when I am Searching for an solution I found this post.When "this.autocomplete = new google.maps.places.Autocomplete(input, {});" this line reach I am getting an error like "InvalidValueError: not an instance of HTMLInputElement"..can u plz hlp me to figure out this error???
@Girija Have you added <script src="maps.googleapis.com/maps/api/…" async defer></script> In your code?
I had to wrap the call to this.setAddress.emit(place); in a NgZone.run(): constructor(private zone: NgZone, ..) in the constructor, then this.zone.run(() => {this.setAddress.emit(place);}) in the event listener. Without it some model to UI view synchronization was not triggered immediately
|
8

@Habeeb's answer is a great start, but this is a cleaner implementation. First install the googlemaps typings npm install --save-dev @types/googlemaps and import them somewhere in your app import {} from '@types/googlemaps'.

import { Directive, ElementRef, EventEmitter, OnInit, Output } from '@angular/core'; @Directive({ // xx is your app's prefix selector: '[xxPlaceLookup]' }) export class PlaceLookupDirective implements OnInit { @Output() onSelect: EventEmitter<any> = new EventEmitter(); private element: HTMLInputElement; constructor(el: ElementRef) { this.element = el.nativeElement; } ngOnInit() { const autocomplete = new google.maps.places.Autocomplete(this.element, { types: ['establishment'], componentRestrictions: {country: 'us'} }); google.maps.event.addListener(autocomplete, 'place_changed', () => { const place = autocomplete.getPlace(); this.onSelect.emit(place); }); } } 

3 Comments

Cleaner implementation.
I had to wrap the call to this.setAddress.emit(place); in a NgZone.run(): constructor(private zone: NgZone, ..) in the constructor, then this.zone.run(() => {this.setAddress.emit(place);}) in the event listener. Without it some model to UI view synchronization was not triggered immediately
what do you mean by somewhere in your app?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.