Angular2 GoogleMapAPI AutoComplete Error Cannot read property 'Autocomplete' - google-maps

I want to use GoogleMapAPI auto-complete in angularjs2 and onsenUI2, but I can't do that.
This is my code:
import { Component, NgModule, OnInit, ViewChild, ElementRef } from '#angular/core';
import { FormControl, FormsModule, ReactiveFormsModule } from "#angular/forms";
import { BrowserModule } from "#angular/platform-browser";
import { AgmCoreModule, MapsAPILoader } from 'angular2-google-maps/core';
// import {GoogleplaceDirective} from '../googleplace.directive';
import {NgModel} from '#angular/forms';
#Component({
selector: 'app-root',
styles: [`
.sebm-google-map-container {
height: 300px;
}
`],
template: `
<google-search-bar></google-search-bar>
<div class="container">
<div class="form-group">
<input placeholder="search for location" autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="form-control" #search [formControl]="searchControl">
</div>
<sebm-google-map [latitude]="latitude" [longitude]="longitude" [scrollwheel]="false" [zoom]="zoom">
<sebm-google-map-marker [latitude]="latitude" [longitude]="longitude"></sebm-google-map-marker>
</sebm-google-map>
</div>
`})
export class GoogleMap implements OnInit {
public latitude: number;
public longitude: number;
public searchControl: FormControl;
public zoom: number;
public fuzzyControl: FormControl;
public address:string;
#ViewChild("search")
public searchElementRef: ElementRef;
constructor(
private mapsAPILoader: MapsAPILoader
) {}
ngOnInit() {
//set google maps defaults
this.zoom = 4;
this.latitude = 39.8282;
this.longitude = -98.5795;
//create search FormControl
this.searchControl = new FormControl();
this.fuzzyControl = new FormControl();
//set current position
this.setCurrentPosition();
this.setMapsAPILoader();
//load Places Autocomplete
this.mapsAPILoader.load().then(() => {
let autocomplete = new google.maps.places.Autocomplete(this.searchElementRef.nativeElement, {
types: ["address"]
});
autocomplete.addListener("place_changed", () => {
//get the place result
let place: google.maps.places.PlaceResult = autocomplete.getPlace();
//set latitude and longitude
this.latitude = place.geometry.location.lat();
this.longitude = place.geometry.location.lng();
});
});
}
private setCurrentPosition() {
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition((position) => {
this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;
this.zoom = 12;
});
}
}
This is Error
Unhandled Promise rejection: Cannot read property 'Autocomplete' of undefined ; Zone: angular ; Task: Promise.then ; Value: TypeError: Cannot read property 'Autocomplete' of undefined(…) TypeError: Cannot read property 'Autocomplete' of undefined

I have been been stuck on this same exact error with code that is very similar to yours.
I am only using the auto complete part of the code (no map display but my input call is the exact same as yours) so I can't completely verify this but the error seems to be caused by this in the input statement:
"#search [formControl]="searchControl"
Looking at this article I noticed how they used an id call within the input: angular2-google-maps autocomplete not working
So I removed that part of my input statement. It should look like this:
<input id="address" placeholder="search for location" autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="form-control">
And used javascript in my auto complete look look for that the id:
this.mapsAPILoader.load().then(() => {
let autocomplete = new google.maps.places.Autocomplete(
<HTMLInputElement>document.getElementById("address"), {
types: ['address']
});
autocomplete.addListener('place_changed', () => {
this.ngZone.run(() => {
// get the place result
let place: google.maps.places.PlaceResult = autocomplete.getPlace();
// add map calls here
});
});
});
After changing that, the error disappears and auto-complete works as hoped. Hope this works for you.
One other thing to check is that you imported your API Key into the correct component. See the article above for reference.

Related

get input values by there class

I have a following angular component ts with an ability to add dynamic fields to a form:
import {Component, ElementRef, OnInit, ViewChild} from '#angular/core';
import {Buildcompany} from "../../models/buildcompany.model";
import {BuildcompanyService} from "../../services/buildcompany.service";
import { FormGroup, FormControl, FormArray, FormBuilder } from '#angular/forms'
#Component({
selector: 'app-add-buildcompany',
templateUrl: './add-buildcompany.component.html',
styleUrls: ['./add-buildcompany.component.css'],
})
export class AddBuildcompanyComponent implements OnInit {
buildcompany: Buildcompany={
shortname:'',
fullname:'',
address:'',
telephone:'',
website:'',
sociallinks:'',
foundationyear:''
}
submitted=false;
#ViewChild('network', {static: true}) networkElement: ElementRef;
netw?: [];
constructor(private buildcompanyService:BuildcompanyService,networkElement: ElementRef) {
this.networkElement=networkElement;
}
ngOnInit(): void {
}
add(){
let row = document.createElement('div');
row.className = 'row';
row.innerHTML = `
<br>
<input type="text" id="netw" name="network[]" #network class="form-control netw">"`;
let row2 = document.createElement('div');
row2.innerHTML = `
<br>
<input type="text" name="links[]" class="form-control">"`;
// #ts-ignore
document.querySelector('.showInputField').appendChild(row);
// #ts-ignore
document.querySelector('.showInputField').appendChild(row2);
}
saveBuildcompany(): void {
// #ts-ignore
this.netw = (<HTMLInputElement>document.getElementsByClassName("netw")).value;
// #ts-ignore
console.log(this.netw[0].value);
const data = {
shortname: this.buildcompany.shortname,
fullname: this.buildcompany.fullname,
address: this.buildcompany.address,
telephone: this.buildcompany.telephone,
website: this.buildcompany.website,
sociallinks: this.buildcompany.sociallinks,
foundationyear: this.buildcompany.foundationyear
};
this.buildcompanyService.create(data)
.subscribe({
next: (res) => {
console.log(res);
this.submitted = true;
},
error: (e) => console.error(e)
});
}
newBuildcompany(): void {
this.submitted = false;
this.buildcompany = {
shortname: '',
fullname:'',
address:'',
telephone:'',
website:'',
foundationyear:''
};
}
}
Is that posible at all to get values of those dynamicly added fields into an array on save() function? I can see in a console that they are actually shown. The class is called netw. Or what is the best approche to this task?
UPDATE
I tryed to add a formgroup and got the following error
Error:
ngModel cannot be used to register form controls with a parent formGroup directive. Try using
formGroup's partner directive "formControlName" instead. Example:

Angular 4 : How to use custom validator with data from back-end

In a form, the button submit becomes enable only when the form is valid. A particular input contains datalist with data from back-end. I want to return the form invalid if data filled by the user is not in the datalist. So, i need a custom validator which check if user data is equal to data from back end.
Data from back-end is a list of objects listOfArticles which contains reference numbers and other data.
So, I try to create a function for custom validator in the same component file, but it doesn't work for 2 reasons :
I don't know how to retrieve the path of my listOfArticles "refer.refNumber.input"
I get error in my console : "Cannot read property 'length' of undefined".
Function for custom validator :
export function ValidateRefNumber(control: AbstractControl) {
for (let refer of ArbologistiqueComponent.listOfArticles) {
if (control.value == refer.refNumber.input) {
return true;
}
}
return null;
}
My entire component.ts :
import { Component, OnInit } from '#angular/core';
import 'rxjs/add/operator/switchMap';
import { ManagementArbologistiqueService } from "../management-arbologistique.service";
import { ActivatedRoute, Params } from '#angular/router';
import { FormGroup, FormControl, FormBuilder, FormArray, Validators } from '#angular/forms';
#Component({
selector: 'app-arbologistique',
templateUrl: './arbologistique.component.html',
styleUrls: ['./arbologistique.component.css']
})
export class ArbologistiqueComponent implements OnInit {
private reponseTest: String;
private listOfArticles :Array<Object>
private pathDownload: any;
private myFormGroup: FormGroup;
fileToUpload: File = null;
private buttonSubmitEnabled: boolean = false;
constructor(public fb: FormBuilder, private managementArbo: ManagementArbologistiqueService, private route: ActivatedRoute) { }
ngOnInit() {
this.myFormGroup = this.fb.group({
itemRows: this.fb.array([this.initItemRows()])
})
this.myFormGroup.valueChanges.subscribe(x => this.buttonSubmitEnabled = false);
this.getListBdd();
}
initItemRows() {
return this.fb.group({
... //other fields
refNb: ['',[Validators.required, ValidateRefNumber]],
... //other fields
})
}
addRow(index: number) {
console.log("functionAddRow called");
const control = <FormArray>this.myFormGroup.controls['itemRows'];
control.insert(index, this.initItemRows());
}
deleteRow(index: number) {
console.log("functionDeleteRow called");
const control = <FormArray>this.myFormGroup.controls['itemRows'];
control.removeAt(index);
}
sendForm() {
this.buttonSubmitEnabled=true;
console.log("functionExportCalled");
this.route.params.subscribe((params: Params) => {
let subroute = "exportation";
this.managementArbo.postProducts(subroute, JSON.stringify(this.myFormGroup.value))
.subscribe(
res => { this.reponseTest = res; console.log('reponse:' + res); }
,
err => console.log(err),
() => console.log('getProducts done'));
});
}
getListBdd() {
this.route.params.subscribe((params: Params) => {
let subroute = "getRefNumber";
this.managementArbo.getProducts(subroute)
.subscribe(
res => { this.listOfArticles = res; console.log('reponse:' + res); }
,
err => console.log(err),
() => console.log('getProducts done'));
});
}
get refNb() {
return this.myFormGroup.get('itemRows.refNb');
}
}
export function ValidateRefNumber(control: AbstractControl) {
for (let refer of ArbologistiqueComponent.listOfArticles) {
if (control.value == refer.refNumber.input) {
return true;
}
}
return null;
}
input with datalist (component.html) :
<input list="refNumbers" formControlName="refNb" type="text" name="article" maxlength="8" size="15" required title="8 characters" />
<datalist id="refNumbers">
<option *ngFor="let ref of listOfArticles">{{ref.refNumber.input}}</option>
</datalist>
I will provide you an answer with a simple example, then you can transform it as you need.
Lets say we have this list of articles:
articles = [
{
id: 1,
content: "test 123"
},
{
id: 2,
content: "test 345"
}
];
And we want to check if the user type in the input text one of the id's of the articles, otherwise, the form is invalid.
We have this little piece of HTML:
<div *ngFor="let article of articles">
{{article | json}}
</div>
<form [formGroup]="cForm" (submit)="submitForm(cForm.value)" novalidate>
<input formControlName="article" type="text" name="article" required />
<input type="submit" value="submit" [disabled]="!cForm.valid">
</form>
So I just print the array for debug, and we have input which has formControlName of article. and we have a button which is disable if the form is invalid.
Init the formGroup:
this.cForm = this._fb.group({
article: [
null,
Validators.compose([Validators.required, matchValues(this.articles)])
]
});
So we have a formGroup with simple validation of required, and our new validator named matchValues.
export const matchValues = (valuesToCheck: any[]): ValidatorFn => {
return (control: AbstractControl): { [key: string]: boolean } => {
const controlValue = control.value;
let res = valuesToCheck.findIndex(el => el.id === +controlValue);
console.log(res);
return res !== -1 ? null : { matched: true };
};
};
I simply wrap it in a function which received the array of articles, and I'm trying to find an article id which is matching the value of the input text. If I dont find any, I'm returning an object:
{ matched: true }
which is the error of the control, so you can access this error with 'matched' same as you access required or minlength or any other error.
I hope its clear enough and gives you a nice kick off to solve your a bit more complicated problem.
--UPDATE--
To make it more clear:
findIndex searching in the array for the first item that match the condition and return the index of the item or -1 if nothing found, in this case, I've check if the value of the input from the user match any ID from the list. so as long as my res is not equal to -1, thats mean that I found an item that match so the validation pass, but if res === -1 there is an error, so I send new object with the error name matched so you can handle it later. anyway in this case, I didn't find any item that matched the id, so its an error.
--UPDATE2--
You got some trouble, So I've included a working example.
Check this out

Mapbox missing Gl JS css

My map is not loading and no error is being displayed in the console.
please help.
this is the error screenshot as shown in the browser
there is no build error while compiling the code neither any error is thrown but the ma form mabox is not loading and is written as - Missing mapbox Gl JS CSS
the following is the code snippet for the same
// code for map.component.ts
import { Component, OnInit } from '#angular/core';
import * as mapboxgl from 'mapbox-gl';
import { MapService } from '../map.service';
import { GeoJson, FeatureCollection } from '../map';
#Component({
selector: 'app-map-box',
templateUrl: './map-box.component.html',
styleUrls: ['./map-box.component.css']
})
export class MapBoxComponent implements OnInit{
/// default settings
map: mapboxgl.Map;
style = 'mapbox://styles/kanavmalik10/cjfbjx6fp70fl2snuphc7zjw2';
lat = 37.75;
lng = -122.41;
message = 'Hello World!';
// data
source: any;
markers: any;
constructor(private mapService: MapService) {
}
ngOnInit() {
this.markers = this.mapService.getMarkers()
this.initializeMap()
}
private initializeMap() {
/// locate the user
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
this.lat = position.coords.latitude;
this.lng = position.coords.longitude;
this.map.flyTo({
center: [this.lng, this.lat]
})
});
}
this.buildMap()
}
buildMap() {
this.map = new mapboxgl.Map({
container: 'map',
style: this.style,
zoom: 13,
center: [this.lng, this.lat]
});
/// Add map controls
this.map.addControl(new mapboxgl.NavigationControl());
//// Add Marker on Click
this.map.on('click', (event) => {
const coordinates = [event.lngLat.lng, event.lngLat.lat]
const newMarker = new GeoJson(coordinates, { message: this.message })
this.mapService.createMarker(newMarker)
})
/// Add realtime firebase data on map load
this.map.on('load', (event) => {
/// register source
this.map.addSource('firebase', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
/// get source
this.source = this.map.getSource('firebase')
/// subscribe to realtime database and set data source
this.markers.subscribe(markers => {
let data = new FeatureCollection(markers)
this.source.setData(data)
})
/// create map layers with realtime data
this.map.addLayer({
id: 'firebase',
source: 'firebase',
type: 'symbol',
layout: {
'text-field': '{message}',
'text-size': 24,
'text-transform': 'uppercase',
'icon-image': 'rocket-15',
'text-offset': [0, 1.5]
},
paint: {
'text-color': '#f16624',
'text-halo-color': '#fff',
'text-halo-width': 2
}
})
})
}
/// Helpers
removeMarker(marker) {
this.mapService.removeMarker(marker.$key)
}
flyTo(data: GeoJson) {
this.map.flyTo({
center: data.geometry.coordinates
})
}
}
<input type="text" [(ngModel)]="message" placeholder="your message...">
<h1>Markers</h1>
<div *ngFor="let marker of markers | async">
<button (click)="flyTo(marker)">{{ marker.properties.message }}</button>
<button (click)="removeMarker(marker)">Delete</button>
</div>
<div class="map" id="map"></div>
You seed to include the GL JS CSS stylesheet. See the quickstart at https://docs.mapbox.com/mapbox-gl-js/overview/#quickstart
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.css' rel='stylesheet' />

angular2-google-maps autocomplete not working

I am trying to add autocomplete to my project with angular2-google-maps. I add AgmCoreModule.forRoot (with libraries: 'places') in my AppModule and then added the autocomplete code in my component. Still I get "Cannot read property 'Autocomplete' of undefined" error. I tried checking the value of global var google, and google.map does not contain 'places' field. I am relatively new to angular2, so would need some help to understand what I am missing. My code is
in AppModule
import { AgmCoreModule } from 'angular2-google-maps/core';
#NgModule({
bootstrap: [ App ],
declarations: [
App,
ErrorComponent,
],
imports: [ // import Angular's modules
AgmCoreModule.forRoot({
apiKey: '[API_KEY_REDACTED]',
libraries: ["places"]
}),
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpModule,
TranslateModule.forRoot(),
RouterModule.forRoot(ROUTES, { useHash: true })
],
providers: [ // expose our Services and Providers into Angular's dependency injection
ENV_PROVIDERS,
APP_PROVIDERS,
]
})
export class AppModule {
In AppComponent:
import {MapsAPILoader} from 'angular2-google-maps/core';
import { NgZone } from '#angular/core';
declare var google: any;
export class EventInfoTab {
#ViewChild('gmap') gmap:any;
constructor(
private _loader: MapsAPILoader,
private zone : NgZone,
)
ngAfterViewInit(): void {
this._loader.load().then(() => {
let address = document.getElementById("location");
console.log("google", google);
let autocomplete = new google.maps.places.Autocomplete(address, {});
console.log ("autocomplete",autocomplete);
google.maps.event.addListener(autocomplete, 'place_changed', () => {
this.zone.run(() => {
console.log ("autocomplete place_changed",autocomplete);
var place = autocomplete.getPlace();
this.lat = place.geometry.location.lat();
this.lng = place.geometry.location.lng();
//alert(JSON.stringify(place));
this.markers[0] ={
lat: this.lat,
lng: this.lng,
label: 'x',
draggable: false
};
});
});
});....
So, I was able to make it work. I was trying to include the map with autocomplete in a feature component, after routing from the main component. I removed the code
'AgmCoreModule.forRoot({
apiKey: '[API_KEY_REDACTED]',
libraries: ["places"]
}),'
from app module.ts and added it in the feature's module.ts imports and it worked.
Since Pooja got hers working, and I had already completed a working example of Angular2 + angular2-google-maps + Autocomplete for her, I thought I'd add the code here to help future developers looking for something similar.
import {
Component,
NgModule,
OnInit,
NgZone
} from '#angular/core';
import {
BrowserModule
} from '#angular/platform-browser';
import {
AgmCoreModule,
MapsAPILoader
} from 'angular2-google-maps/core';
declare var google: any;
#Component({
selector: 'my-app',
styles: [`
.sebm-google-map-container {
height: 300px;
}
`],
template: `
<sebm-google-map
[latitude]="lat"
[longitude]="lng"
[zoom]="zoom"
[disableDefaultUI]="false"
[zoomControl]="true">
<sebm-google-map-marker
*ngFor="let m of markers; let i = index"
(markerClick)="clickedMarker(m.label, i)"
[latitude]="m.lat"
[longitude]="m.lng"
[label]="m.label"
[markerDraggable]="m.draggable"
(dragEnd)="markerDragEnd(m, $event)">
<sebm-google-map-info-window>
<strong>InfoWindow content</strong>
</sebm-google-map-info-window>
</sebm-google-map-marker>
</sebm-google-map>
<input type="text" id="autocompleteInput">
`})
export class App implements OnInit {
constructor(
private _loader: MapsAPILoader,
private _zone: NgZone) {
}
ngOnInit(): void {
this.autocomplete();
}
autocomplete() {
this._loader.load().then(() => {
var autocomplete = new google.maps.places.Autocomplete(document.getElementById("autocompleteInput"), {});
google.maps.event.addListener(autocomplete, 'place_changed', () => {
this._zone.run(() => {
var place = autocomplete.getPlace();
this.markers.push({
lat: place.geometry.location.lat(),
lng: place.geometry.location.lng(),
label: place.name,
});
this.lat = place.geometry.location.lat();
this.lng = place.geometry.location.lng();
console.log(place);
});
});
});
}
// google maps zoom level
zoom: number = 8;
// initial center position for the map
lat: number = 51.673858;
lng: number = 7.815982;
clickedMarker(label: string, index: number) {
console.log(`clicked the marker: ${label || index}`)
}
mapClicked($event: MouseEvent) {
this.markers.push({
lat: $event.coords.lat,
lng: $event.coords.lng
});
}
markerDragEnd(m: marker, $event: MouseEvent) {
console.log('dragEnd', m, $event);
}
markers: marker[] = [];
}
// just an interface for type safety.
interface marker {
lat: number;
lng: number;
label?: string;
draggable: boolean;
}
#NgModule({
imports: [
BrowserModule,
AgmCoreModule.forRoot({
libraries: ['places']
})
],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
Working Plnkr
4/17/2017 UPDATE
In version 1.0.0-beta.0 - green-zebra, the AGM team released a breaking change with the naming of their components which will require an update to your template file as follows:
<agm-map
[latitude]="lat"
[longitude]="lng"
[zoom]="zoom"
[disableDefaultUI]="false"
[zoomControl]="true">
<agm-marker
*ngFor="let m of markers; let i = index"
(markerClick)="clickedMarker(m.label, i)"
[latitude]="m.lat"
[longitude]="m.lng"
[label]="m.label"
[markerDraggable]="m.draggable"
(dragEnd)="markerDragEnd(m, $event)">
<agm-info-window>
<strong>InfoWindow content</strong>
</agm-info-window>
</agm-marker>
</agm-map>
<input type="text" id="autocompleteInput">
An updated version of the above plnkr/code can be found in the following GitHub repo.
I also have the same issue, but different cause. I use the map inside modal (pop-up)
let address = document.getElementById("location");
let autocomplete = new google.maps.places.Autocomplete(address, {});
In my case the above code run inside ngOnInit, the problem is the text input element is still not created.
You need to put the autocomplete code inside a function which runs each time the elements are created. I tried ngAfterViewInit, but didn't work

Angular2/ionic2 angular2-google-maps error Cannot find name 'google'

I followed the this example (click here) to make a field of address with autocompletion of google map Places,
But it's giving the following error:
Can not find name 'google'.
L53: this.mapsAPILoader.load (). Then (() => {
L54: let autocomplete = new google.maps.places.Autocomplete
(this.searchElementRef.nativeElement, {
I tried to install google-maps types npm install --save #types/google-maps but it is without results.
After installing #types/google-maps the build is ok but when I luanch i have this error :
Cannot find name 'google' after installing #types/google-maps
My Code :
import { FormControl } from '#angular/forms';
import { Component, ViewChild, OnInit, ElementRef } from '#angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { MapsAPILoader } from 'angular2-google-maps/core';
#Component({
selector: 'page-page2',
templateUrl: 'page2.html'
})
export class Page2 implements OnInit {
latitude: number = 51.678418;
longitude: number = 7.809007;
zoom: number = 4;
searchControl: FormControl;
#ViewChild("search")
searchElementRef: ElementRef;
constructor(public navCtrl: NavController, public navParams: NavParams, private mapsAPILoader: MapsAPILoader) {
// some not related to this question code
}
ngOnInit() {
//create search FormControl
this.searchControl = new FormControl();
//set current position
this.setCurrentPosition();
//load Places Autocomplete
this.mapsAPILoader.load().then(() => {
let autocomplete = new google.maps.places.Autocomplete(this.searchElementRef.nativeElement, {
types: ["address"]
});
autocomplete.addListener("place_changed", () => {
//get the place result
let place: google.maps.places.PlaceResult = autocomplete.getPlace();
//set latitude and longitude
this.latitude = place.geometry.location.lat();
this.longitude = place.geometry.location.lng();
});
});
}
private setCurrentPosition() {
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition((position) => {
this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;
this.zoom = 12;
});
}
}
}
Run typings install dt~google.maps --global
as found here