Google places autocomplete inside a react component - google-maps

I am trying to build a google map component and everything is working fine with the Google Maps API v3 but not the Autocomplete functionality.
This is the code I am using:
The Google Map component
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
const Marker = React.createClass({
componentDidMount: function() {
console.log("Marker on mount");
},
render: function() {
return false;
}
});
export default Marker;
const GoogleMap = React.createClass({
componentDidMount: function() {
var mapOptions = {
center: this.createLatLng(this.props.center),
zoom: this.props.zoom || 14
};
var map = new google.maps.Map(ReactDOM.findDOMNode(this), mapOptions);
//Render all the markers (children of this component)
React.Children.map(this.props.children, (child) => {
var markerOptions = {
position: this.createLatLng(child.props.position),
title: child.props.title || "",
animation: google.maps.Animation.DROP,
icon: child.props.icon || null,
map: map,
autocomplete:new google.maps.places.AutocompleteService()
};
var marker = new google.maps.Marker(markerOptions);
if(child.props.info) {
var infowindow = new google.maps.InfoWindow({
content: child.props.info
});
marker.addListener('click', function() {
infowindow.open(map, marker);
});
}
});
var input = this.refs.search;
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
var autocomplete = new google.maps.places.Autocomplete(input);
autocomplete.bindTo('bounds', map);
this.setState({map});
},
createLatLng: function(element) {
return new google.maps.LatLng(element.lat, element.lng);
},
render: function() {
return (
<div className="map">
<input ref="search"/>
</div>
)
}
});
export default GoogleMap;
And this is where I call the component
import React, {Component} from 'react';
import GoogleMap from './GoogleMap';
import Marker from './GoogleMap';
import Geosuggest from 'react-geosuggest';
class DoodlesMap extends Component {
state = {
center: null,
marker: null,
directions: null
};
componentWillMount() {
navigator.geolocation.getCurrentPosition((position) => {
this.setState({
center: {
lat: position.coords.latitude,
lng: position.coords.longitude
},
marker: {
position: {
lat: position.coords.latitude,
lng: position.coords.longitude
}
}
});
});
}
renderYouAreHereMarker() {
return (
<Marker
position={this.state.center}
icon="../../img/you-are-here.png"
/>
)
}
render() {
if (!this.state.center) {
return (
<div>Loading...</div>
)
}
return (
<div>
<GoogleMap
center={this.state.center}
zoom={15}
>
{this.renderYouAreHereMarker()}
<Marker
position={{lat: 41.317334, lng: -72.922989}}
icon="../../img/marker.png"
info="Hola"
/>
<Marker
position={{lat: 41.309848, lng: -72.938234}}
icon="../../img/marker.png"
info="Epi"
/>
</GoogleMap>
</div>
);
}
}
export default DoodlesMap;
I do not receive any console error. The map is displayed correctly (with the markers as children) the input also, but does not make the autocomplete.
Thank you in advance!!

I figure it out, and was very simple issue.
The thing was that I did not "enable" the places API in my google developer console.
Once I did this everything worked fine!!

It took me quite some time to get the google-places-autocomplete feature working nicely with a React Component. I did however manage to figure it out and wrote a short tutorial on it over here.
Medium Tutorial Post!
TL;DR of tutorial: You have to use a library called react-scripts to render the google-maps-places library after the component has mounted. Most of the time the reason that the autocomplete doesn't work is because the library did not load properly.

Related

how do I filter pins on Google Map pulled from a json api endpoint using Vue

I have a map with a number of pins on it, the pins are generated from an endpoint api (json). I want to filter the pins via an input that has a v-modal - the search criteria is already set up and is pulled from the same api.
Even if someone can give some tips as to where in the vue lifecycle the filter should happen, i.e mounted, updated computed ect
Originally I used this article as a reference
https://medium.com/#limichelle21/integrating-google-maps-api-for-multiple-locations-a4329517977a
created() {
axios
.get(
`https://cors-anywhere.herokuapp.com/myEndpoint`
)
.then(response => {
// JSON responses are automatically parsed.
this.allProperties = response.data;
this.markers = this.allProperties.map(function (x) {
return {
lat: parseFloat(x.lat).toFixed(3),
lng: parseFloat(x.lng).toFixed(3),
add: x.dispAddress,
propId: x.property_id,
propPrice: x.outputAskingPrice,
propImg: x.imagePath
};
});
this.allProperties = response.data.map(x => {
x.searchIndex = `${x.sellingStatus} ${x.priceType} ${x.typeNames[0]} ${x.typeNames[1]} ${x.dispAddress}`.toLowerCase();
return x;
});
});
},
mounted: function () {
var _this = this;
function initMap() {
var center = {
lat: 53,
lng: -3
};
var map = new google.maps.Map(document.getElementById("map-canvas"), {
zoom: 10,
center: center
});
var newPin = new google.maps.Marker({
position: center,
map: map
});
}
},
updated() {
var _this = this;
var map = new google.maps.Map(document.getElementById("map-canvas"), {
zoom: 9,
center: new window.google.maps.LatLng(55.961, -3)
});
var infowindow = new google.maps.InfoWindow({});
var newPin;
var count;
for (count = 0; count < _this.markers.length; count++) {
newPin = new google.maps.Marker({
position: new google.maps.LatLng(
_this.markers[count].lat,
_this.markers[count].lng
),
map: map,
icon: "../assets/img/map-pin.png"
});
google.maps.event.addListener(
newPin,
"click",
(function (newPin, count) {
return function () {
infowindow.setContent(` ${_this.markers[count].add} <p> ${_this.markers[count].propPrice}</p><img src="${_this.markers[count].propImg}"><p>`);
infowindow.open(map, newPin);
};
})(newPin, count)
);
}
If you have v-model on an <input> field like mentioned in your question, you are binding the value of this <input> field to a variable probably defined in the data part of your Vue component. The value is always up to date in the model (reactive binding). You can watch this value and then trigger a function which updates Google Maps. Here is an example:
Vue.component('demo', {
data () {
return {
inputField: ''
};
},
created () {
console.log('Component script loaded, HTML not yet ready, load the data from your backend. Use a flag like isLoading or similar to indicate when the data is ready to enable input.');
},
mounted () {
console.log('Component mounted, HTML rendered, load Google Maps');
},
watch: {
inputField (newValue) {
console.log(`inputField changed to ${newValue}. Trigger here a method which update Google Maps. Make sure to debounce the input here, so that it does not trigger a Google Maps update too often.`);
}
},
template: `
<div>
<input type="text" v-model="inputField" placeholder="Lookup place">
</div>`
});
new Vue({ el: '#vue-demo-container' });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue-demo-container">
<demo></demo>
</div>

how to add google map as a ui component in magento 2

i need to display a coordinates field , coming from database, currently am displaying it as an input like this:
i need to display it as map like this:
i googled a LOT and lot of answers on how to load the map using requireJS , but non is working with Magento 2.1..
currently am at this stage:
requirejs.config({
paths: {
googlemaps: 'googlemaps',
async: 'requirejs-plugins/src/async'
},
googlemaps: {
params: {
key: 'xxxxxxxxxxxxxx'
}
}
});
define([
'Magento_Ui/js/form/element/abstract',
'jquery',
'googlemaps!'
],function(Abstract,$,gmaps) {
return Abstract.extend({
initialize: function () {
return this._super();
},
initMap: function() {
console.log(this.value());
console.log("-------------");
var uluru = {lat: -25.363, lng: 131.044};
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: uluru
});
var marker = new google.maps.Marker({
position: uluru,
map: map
});
},
});
});
now , question is how to call the initMap function after page is loaded ?
help pleaaaaase

grey box google maps and angular 2

I have a problem with angular 2 and the API v3 of google maps, the problem is that when I load the page through the router outlet, the map is a grey box, but when I refresh the page the map load correctly, I'm using the js library import on the index page, and the map is loaded in a component.
this is the view of the map
I tried with trigger rezise but don't work. I think that is a problem with the library load or similar.
this is the code:
import {Component, OnInit} from "angular2/core";
import {MapsService} from "../maps/maps.service";
import {mapsIconService} from "./maps.icon.service";
import {BrowserDomAdapter} from 'angular2/platform/browser';
#Component({
selector: "div-map",
template: `
<div id="map" style="height: 500px" class="maps"> </div> `,
providers: [MapsService, mapsIconService, BrowserDomAdapter],
style: `
.maps{
overflow:visible;
}
`
})
export class Maps {
map;
snappedCoordinates = [];
points:string = "";
map:Map;
drawingManager;
placeIdArray = [];
polylines = [];
markers = [];
placeIds = [];
infoWindows = [];
snappedPolyline;
showMap = false;
lineSymbol = {
path: google.maps.SymbolPath.CIRCLE,
scale: 8,
strokeColor: '#005db5',
strokeWidth: '#005db5'
};
mapOptions = {
zoom: 15,
center: {lat: 37.38658313258027, lng: -122.05207727132837},
};
constructor(private _dom:BrowserDomAdapter, private _mapService:MapsService, private _mapIconService:mapsIconService) {
// google.maps.visualRefresh = true;
}
drawSnappedPolyline(snappedCoordinates) {
this.snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: 'black',
strokeWeight: 3
});
this.polylines.push(this.snappedPolyline);
}
animateCircle(polyline) {
var count = 0;
// fallback icon if the poly has no icon to animate
var defaultIcon = [
{
icon: this.lineSymbol,
offset: '100%'
}
];
window.setInterval(function () {
count = (count + 1) % 300;
var icons = defaultIcon;
icons[0].offset = (count / 3) + '%';
polyline.set('icons', icons);
}, 300);
}
setMarker(lat, lng, id) {
var marker = new google.maps.Marker({
position: this.map.getCenter()/*new google.maps.LatLng(lat, lng)*/,
icon: {url: 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(this._mapIconService.getIcon(id))},
draggable: false,
map: this.map
});
}
SetAllMarkers(points) {
for (var i in points) {
this.setMarker(points[i].lat, points[i].lng, points[i].id);
}
}
ngOnInit() {
this.map = new google.maps.Map(this._dom.query("#map"), this.mapOptions);
google.maps.event.trigger(this.map, 'resize');
this._mapService.goData().subscribe(data=> {
this.drawSnappedPolyline(this._mapService.processSnapToRoadResponse(data));
this.setMarker(37.38658313258027, -122.05207727132837, 0);
this.snappedPolyline.setMap(this.map);
this.animateCircle(this.snappedPolyline);
});
}
}
any one have a idea about this? thank in advance
the problem was semantic UI, because he insert the code into a "pusher" and this cause a problem with the all elements

Reassign google map var to new div

I'm using react and I have a component that renders a google map view when it mounts.
I'd like to save the google map var globally and when so that when the component is unmounted then remounted I can just reassign the map to a div instead of recreating it. Is this possible?
So Something like this
/** #jsx React.DOM */
var React = require('react');
window.coverage_map = null;
var CoverageMap = React.createClass({
componentDidMount: function(){
if(window.coverage_map == null){
var ele = React.findDOMNode(this.refs.map);
window.coverage_map = new google.maps.Map(ele, {
center: {lat: 37.7833, lng: -122.4167},
mapTypeControlOptions: {
mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'map_style']
}
});
}
else{
//reassign google map
}
},
render(){
// render code
},
})
No - the root element of a map cannot be changed once instantiated.

Google Maps API - Show multiple marker tool tips

I have a map with markers on it, but I want to be able to show multiple tool tips.
It seems when I do:
marker.openInfoWindowHtml(strToolTip);
...each time it is called, it closes the previous tool tip.
Any ideas how I show multiple marker tool tips on the same map?
Thanks
Have you tried creating a new infowindow object on marker click event and opening it?
var infowindow = new google.maps.InfoWindow({ content: 'Hello world' });
infowindow.open(map, marker);
You can try this:
var markers = [
{ lat: 28.7051, lng: 77.1125 },
{ lat: 28.7081, lng: 77.1075 },
{ lat: 28.7021, lng: 77.1315 }
]
var index=0;
markers.forEach(function (marker) {
var self=this;
(function (marker) {
let mark = new google.maps.Marker({ position: new google.maps.LatLng(marker.lat, marker.lng) });
var infowindow = new google.maps.InfoWindow({ content: index });
infowindow.open(self.map, marker);
mark.setMap(self.map);
index++;
})(marker)
})
Note: The sequence of open() & setMap() must be like above/below code.
Ex:
infowindow.open(self.map, marker);
mark.setMap(self.map);
Snapshot are below:
If you are using angular2/4/5 then have a look on the complete code:
map.component.ts:
import { Component, ViewChild } from '#angular/core';
import { } from '#types/googlemaps'; // You need to install #types/googlemaps, To know how hit this URL- https://medium.com/#svsh227/integrate-google-api-map-in-your-angular-2-4-5-app-472bf08fdac
#Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent {
#ViewChild('map') gmapElement: any;
map: google.maps.Map;
ngOnInit() {
var markers = [
{ lat: 28.4685, lng: 77.0056, toolTip: 'Here too' },
{ lat: 28.4795, lng: 77.0276, toolTip: 'Here too' },
{ lat: 28.4605, lng: 77.0546, toolTip: 'Here too' }
]
// For center
var mapProp = {
center: new google.maps.LatLng(28.4595, 77.0266),
zoom: 13,
mapTypeId: google.maps.MapTypeId.HYBRID // also use ROADMAP,SATELLITE or TERRAIN
};
this.map = new google.maps.Map(this.gmapElement.nativeElement, mapProp);
var marker = new google.maps.Marker({ position: mapProp.center });
marker.setMap(this.map);
var infowindow = new google.maps.InfoWindow({ content: "Hey !! Here we are" });
infowindow.open(this.map, marker);
this.setMultipleMarker(markers, this);
}
setMultipleMarker(markers, self) {
markers.forEach(function (marker) {
(function (marker) {
let mark = new google.maps.Marker({ position: new google.maps.LatLng(marker.lat, marker.lng) });
let infowindow = new google.maps.InfoWindow({ content: marker.toolTip });
infowindow.open(self.map, mark);
mark.setMap(self.map);
})(marker)
})
}
}
map.component.html:
<div>
<br />
<h1>
<span class="heading-text">Here We Are</span>
</h1>
<div class="row" class="card-details rcorners3 card-height">
<div class="card" class="subAbout tech-stack">
<div class="card-header card-header-us">
<div id="map" #map></div>
</div>
</div>
</div>
</div>
And here is the output/Snapshot: