#WebModules #ServerSide #GoogleMaps #GeoLocation #3rdPartyServices
Note: The app presented in this post is now available as an Example template that can be loaded into the Wix Editor. See the post Example: Using the Places API from Google Maps services.
:: Places example application (without map) ::
Here is a screenshot of what the app looks like while typing in a location for lookup. Notice the dropdown menu, created with a Repeater, directly under the text input field.
After the app performs the lookup (using Google Maps web services), the result looks like this:
Another thing you'll notice is that I chose perfectly barfy colors. My excuse is that I'm a developer/writer and not a designer.
Demonstrates
Web modules - server-side (backend) code
Accessing 3rd Party Services (using backend function)
fetch() in the wix-fetch API
getCurrentGeolocation() in the wix-window API to retrieve the user's IP-based location coordinates
wix-storage API for persistent storage of the selected location
Repeater (create list, expand / collapse)
Required for this example
In order to run this example, you will need to supply your own Google Maps API key.
About this example app
There are many instances when you want to find the exact coordinates of a location, or given the coordinates, discover the name of the location. In this article, we demonstrate geolocation queries using the Google Maps API web services to perform location details lookups and reverse location lookups. This example also demonstrates how to call these backend Web modules. Web modules are quite useful when you have security concerns such as protecting your code from prying eyes and preventing access to your web services API keys.
If you don’t have a location currently defined for the app, the getCurrentGeolocation() function of the wix-window API retrieves the current location based on your local IP address. After retrieving the coordinates of your location, the reverse() function (in the backend) invokes the reverse geolocation lookup from the Google Maps service to get the name and details of your location. You can always retrieve your current location by clicking on the “place” icon next to the input field.
You can retrieve details of other locations by entering in the location name in the text input field. As you type, the autocomplete() function (in the backend) will return predictions (I call them “suggestions”) that are supplied by the Google Maps service. The list of suggestions is displayed in a repeater which is displayed directly under the input field. If one of the listed locations is the one you want, click on that name. Otherwise, continue to enter in your name. The more you type, the more targeted the suggestions list becomes.
Once you’ve selected a location, or clicked on the “current location” icon, the details() function is called asking Google Maps to supply details about your desired location. The code then saves your latest selection in persistent storage using the wix-storage API.
App Web Page
We start by creating a simple page with the following components:
Page components details:
repeater1 has two text fields and is used as a dropdown menu.
● text1 is a suggestion list item (place name):
● text2 is a hidden field that holds the unique place ID assigned by Google:
repeater2 has two text fields and displays details of the selected place.
● text3 displays the title of the place detail:
● text4 displays the value of the place detail:
Backend Functions
Create a web module (backend) file named gapi.jsw (short for Google API).
In this file, create three web modules which will be used to call the Google Maps online service functions. Web modules are required to have a .jsw extension. We use the wix-fetch API to access the online services, so we will need an import statement at the top of the file:
import {fetch} from 'wix-fetch';
Google requires an API key to access their services. Get an API key of your own, and then add it to the file directly after the import statement:
const key = "Crazy-long_Google_API-key"; // your own API key here
Place autocomplete suggestions
This web module function returns a list of place "suggestions" based on the user's input in the textInput field.
const apart1 = "https://maps.googleapis.com/maps/api/place/autocomplete/json?";
const apart2 = "&types=(cities)&key=";
export function autocomplete(string) {
let input = "input=" + string;
let url = apart1 + input + apart2 + key;
return fetch (url, {method: 'get'}).then( (httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
}
});
}
Retrieve details of the selected location
This web module function uses the unique place_id to retrieve details for the current or selected location.
const dpart1 = "https://maps.googleapis.com/maps/api/place/details/json?";
const dpart2 = "&key=";
export function details(id) {
let placeid = "placeid=" + id;
let url = dpart1 + placeid + dpart2 + key;
return fetch (url, {method: 'get'}).then( (httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
}
});
}
Reverse geolocation
This web module function returns location name(s) based on the location's coordinates.
const rpart1 = "https://maps.googleapis.com/maps/api/geocode/json?";
const rpart2 = "&key=";
export function reverse(lat, lng) {
let latlng = "latlng=" + lat + "," + lng;
let url = rpart1 + latlng + rpart2 + key;
return fetch (url, {method: 'get'}).then( (httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
}
});
}
Page Code
Now that we have the backend functions, we have to wire up everything and call these backend functions from the web page itself. In the Page Code of the app, the first thing we need to do is to import the Wix APIs that we are using, and our web modules (backend).
import wixWindow from 'wix-window';
import {local} from 'wix-storage';
import {autocomplete} from 'backend/gapi';
import {details} from 'backend/gapi';
import {reverse} from 'backend/gapi';
$w.onReady()
In the page’s $w.onReady() function, we configure the behavior of the repeaters, and set up the page.
$w.onReady(function () {
// handle each suggestion repeater item
$w("#repeater1").onItemReady( ($w, itemData, index) => {
const text1 = $w("#text1");
text1.text = itemData.text1;
const text2 = $w("#text2");
text2.text = itemData.text2;
});
$w("#repeater1").collapse(); // hidden on page load
// handle each location detail line
$w("#repeater2").onItemReady( ($w, itemData, index) => {
const text3 = $w("#text3");
text3.text = itemData.text3;
const text4 = $w("#text4");
text4.text = itemData.text4;
});
$w("#repeater2").hide(); // hidden on page load
// retrieve saved location (if exists) from local storage
let id = local.getItem("place_id");
if(id === undefined || id === null || id.length === 0) {
// if no location saved, find the IP-based geographic location
geoloc();
}
else {
// if a location was saved in local storage, get the details
set_details(id);
}
});
Autocomplete suggestions with a "homemade" dropdown
As you type a location name in the text input field, the onKeyPress event calls the autocomplete() web module to retrieve a list of place suggestions (predictions). The suggestions received from Google Maps will be presented in a dropdown created with a $w.Repeater component.
export function input1_keyPress(event, $w1) {
$w("#repeater2").data = [];
$w("#repeater2").hide();
setTimeout(() => {
// use the current value to get a list of location suggestions
// we call the autocomplete() web module from the backend
let val = event.target.value;
if(val.length === 0)
return; // ignore if empty
autocomplete(val).then(function (resp) {
// create an array of suggestions for the repeater
let predictions = resp.predictions;
let suggestions = [];
predictions.forEach(function (prediction) {
let item = { "_id": prediction.id, "text1": prediction.description, "text2": prediction.place_id };
suggestions.push(item);
});
$w("#repeater1").data = suggestions; // add the suggestions to the repeater
$w("#repeater1").expand(); // we have data so we can expand the repeater
});
}, 10);
}
Select a location from the Repeater dropdown menu
When you click on one of the suggestions displayed by Repeater dropdown menu, the following function is triggered to retrieve the details of the newly selected location.
// this function is triggered when clicking on a suggestion list repeater item
export function container1_click(event, $w) {
let place_id = $w("#text2").text;
set_details(place_id);
$w("#repeater1").collapse();
}
Get the details of the location
This function calls the details() web module to retrieve the details (city, country, utc, etc) of the selected or saved location.
function set_details(val) {
details(val).then(function(resp) {
// find the city (locality) and country of the location
let place = resp.result;
var filtered_array = place.address_components.filter(function(address_component){
return address_component.types.includes("country");
});
var country = filtered_array.length ? filtered_array[0].long_name: "";
filtered_array = place.address_components.filter(function(address_component){
return address_component.types.includes("locality");
});
var locality = filtered_array.length ? filtered_array[0].long_name: "";
console.log("details: " + locality);
let name = place.formatted_address;
let id = place.place_id;
let utc = place.utc_offset;
let lat = place.geometry.location.lat;
let lng = place.geometry.location.lng;
// save the details of the location with wix-storage
local.setItem("place_city", name);
local.setItem("place_lat", lat);
local.setItem("place_lng", lng);
local.setItem("place_utc", utc);
local.setItem("place_id", id);
$w("#input1").value = name; // set input field to location
// array of location detail items for the repeater
let detailsList =
[
{
"_id": "1",
"text3": "place name",
"text4": name
},
{
"_id": "2",
"text3": "latitude",
"text4": "" + lat
},
{
"_id": "3",
"text3": "longitude",
"text4": "" + lng
},
{
"_id": "4",
"text3": "utc",
"text4": "" + utc
},
{
"_id": "5",
"text3": "place id",
"text4": id
}
];
// set the details repeater data and show it
$w("#repeater2").data = detailsList; // add data to our repeater
$w("#repeater2").show();
});
}
Get current IP-based location
If you click on the “here” icon next to the text input field, the vectorImage1_click() function is triggered and calls the geoloc() function to retrieve your IP-based location.
export function vectorImage1_click(event, $w) {
// clear the details to get ready for the "here" location
$w("#repeater2").data = [];
$w("#repeater2").hide();
geoloc();
}
This function calls the getCurrentGeolocation() in the wixWindow API to retrieve your IP-based location. If the Google Maps service does not return any location (ZERO_RESULTS), then the default will be Pittsburgh, PA.
export function geoloc() {
wixWindow.getCurrentGeolocation()
.then( (obj) => {
let lat = obj.coords.latitude;
let lng = obj.coords.longitude;
reverse(lat, lng).then(function(resp) {
let status = resp.status;
if(status === "ZERO_RESULTS") {
let name = "Pittsburgh, PA, USA";
local.setItem("place_city", name);
local.setItem("place_lat", "40.44062479999999");
local.setItem("place_lng", "-79.9958864");
local.setItem("place_utc", "-300");
local.setItem("place_id", "ChIJA4UGSG_xNIgRNBuiWqEV-Y0");
$w("#input1").value = name;
return;
}
let results = resp.results;
var country = null, city = null, place_id = null;
var c, lc, component;
for (var r = 0, rl = results.length; r < rl; r += 1) {
let result = results[r];
// look for city (locality) and country
if (!city && result.types[0] === 'locality') {
for (c = 0, lc = result.address_components.length; c < lc; c += 1) {
component = result.address_components[c];
if (component.types[0] === 'locality') {
city = component.long_name;
continue;
}
if (component.types[0] === 'country') {
country = component.long_name;
if(city && country)
break;
}
}
}
else {
continue;
}
if (city && country) {
place_id = result.place_id;
set_details(place_id);
break;
}
}
});
} )
.catch( (error) => {
let errorMsg = error;
console.log(errorMsg);
});
}
This is a very old thread that is not being monitored. If you have any questions or comments, please create a new post in Community Discussion. Thank you!
Hello there
wonder if someone can help me
i have a data collection that stores address and postcode from users.
I need a map which is connected to the same data collection. each time a new user Signs up, I want a pin in map showing the location that is stored all automaticall. is it possible?
Hello,
I opened the project in the editor, then in preview mode. It doesn't seem to work. I have a lot of CORS errors in the browser console.
Is there a solution on this problem?
Hello,
Thank you for the code. Is there any way to limit the prediction results for a specific country? I do not know where to put it. Cheers.
Is there a way to use location based pricing through wix booking?
Thank You For your Code. Has really helped me by reducing the coding by miles.
I'm also making flight booking web page, I already have my API from RapidApi for autosuggest. It's connecting, the issue i get is, the drop down isn't coming out. I've manipulate and manipulated and nothing is coming up. I've used your code where needed and it still doesn't come up.
$w.onReady(function () { // handle each suggestion repeater item $w("#repeater1").onItemReady( ($w, itemData, index) => { const text29 = $w("#text29"); text29.text = itemData.text29; const text31 = $w("#text31"); text31.text = itemData.text31; }); $w("#repeater1").collapse(); // hidden on page load }); //
let debounceTimer; export function origin_keyPress(event, $w1) { if (debounceTimer) { clearTimeout(debounceTimer); // ignore error debounceTimer = undefined; } debounceTimer = setTimeout(() => { // ignore error // use the current value to get a list of location suggestions // we call the autocomplete() web module from the backend let val = event.target.value; if(val.length === 0) return; autosuggestion(val).then(function (resp) { // create an array of suggestions for the repeater let predictions = resp.predictions; let suggestions = []; predictions.forEach(function (prediction) { let item = { "_id": prediction.Places[0].PlaceId, "text29": prediction.Places[0].PlaceName, "text31": prediction.Places[0].CountryName }; suggestions.push(item); }); $w("#repeater1").data = []; // clear the repeater contents $w("#repeater1").data = suggestions; // add the suggestions to the repeater $w("#repeater1").expand(); // we have data so we can expand the repeater }); }, 150); }
I have a repeater with items that have longitude and latitudes. I want the 3 closest locations to automatically display based on the visitors location. Is that possible with this code?
Yisrael, if we wanted to to check the directions tab within the map how do we stop it defaulting to the the wix.com address? The rest of my code works great ...
Hi All/Yisrael,
Code works like a charm, only thing is it cuts in and out as it generates the suggestions. Any way to smooth that?
Thanks,
Anney
Yisrael, like always, thanks champ. Greatly detailed yet concise post.
This has helped me slash off several crucial features that were left to figure out on my site! Thank you all for your awesome contributions.
See Example: Multiple Markers Google Maps.
Yisrael,
I have a dynamic page with a repeater. I attempted to use also the html embed widget and pasted in google iframe code in order to embed map. Apparently the Wix html widget does not work well with Wix Code. Thoughts/Suggestions?
Yisrael .... you are always so good to us : )
Thank you <3
Note: The app presented in this post is now available as an Example template that can be loaded into the Wix Editor. See the post Example: Using the Places API from Google Maps services.
@iosdev1024 The goal of this article is to introduce you to coding with Google Maps Services. Try to understand how it works and then copy/paste the code in your own project.
This is what everyone have done here
Good luck,
William
Hello,
Is all this code and website available somewhere so i can just copy the whole thing that already works, and then modify it for my use n my own wix page?
Thanks!
Hi Raj,
For more information on your questions on distance and street addresses, you should refer to Google's API documentation where are of the different query possibilities are explained and documented.
Good luck,
Yisrael
Hello @Yisrael
Can you please tell me how do i get autocomplete for street addresses i type in the user input field as this one only returns city names. Do i change the type attribute in the maps API url ? if so, what type should i use ?
Thank you so much for the explanation @Yisrael it helped a lot. There is another question though, How do i calculate distance between those two places and display in a text box ?