How to Use Google Maps Services in Wix Code

#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

Required for this example

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);
   });
}
7 Likes

If I close my eyes and don’t read it ---- maybe I won’t fall in love!

That was so great!!Thank you!!!

Hi Yisrael, thank you for this amazing post. I would like to use it for a flight booking form so customers can type in where they want to go and where they are flying from to complete the input. Therefore I don’t think I need the second repeater? I want to be able to email whatever they have as the completed fields as well.

I’m a little unsure on how to setup the backend files? Would you be able to break this down a bit more for me? I have started with the gapi file, but unsure how to link the 3 web modules? Thanks so much

@TailoredWebDesign - here’s a screenshot that might help:


This screenshot shows the Site Structure with the gapi.jsw file, and two of the functions that we use to talk to the Google Maps web services.

oh ok thanks, that’s what I did? But getting errors:

Url is http://pete092.wixsite.com/book-the-best/cheap-flights1

Thanks Yisrael

Your quote (" not ”) and tick (’ not ’) marks are incorrect. Don’t use the “closing” (slanted) marks.

I’m sorry not quite sure what u mean, I just copied your code?

Sorry, my mistake. I fixed my code examples. Somewhere along the line the ’ and " got switched to invalid characters.

Try copying those lines again.

Thanks that fixed it! Now just some page errors…

Hey, have you ever considered going into QA? :upside_down_face:

Same thing (corrupted ') happened to my page code. Fixed!

Haha love to!

Thank you that all works now…

Now for the tough one. I would like to just populate the field, select from suggestions, then that selection gets sent to my database and email. This was already setup to my input, but will it work like this too?

Do I need that second repeater, I assume not? And I don’t think I would use current location, can I just delete those parts without breaking the code? Thanks a lot Ysirael, very cool!!

You should be able to just get rid of the second repeater. As you stated, the value that you want is in the input field and you can use that for whatever you want.

Works perfectly! Now, I would like to disable the geolocation but feel it will break the other parts? I won’t need it for this site.
And I would also like to use for the origin input on the same page. Is that possible on the same page? Thank you so much again, this is so cool!

*edit - hmm actually when I select from the dropdown it doesn’t autopopulate the input field?

https://pete092.wixsite.com/book-the-best/cheap-flights1

The purpose of the example was to show different services available from Google Maps. I tied them together so that the example would be somewhat coherent, but their tied together loosely to allow the developer to pick and choose.

No need to use geolocation - that’s just for someone who wants their current location according to their IP address.

Using the local wix-storage, the currently selected location is saved for use on all pages of the site until another location is selected. You can change this to session so that the location is only saved for the user’s session. You can even save the location as a field in the database to be used for reference. It’s all up to you how you want to use it.

I’m glad you like the example.

Have fun!

SOLVED
Hi, Thank you vary much for help!!!
How can i make it in other language ( Hebrew ).
Thank you again!

Hi Ivan,

You can read about Google Maps language support to see what languages are supported (Hebrew is in the list) and how it’s done.

Have fun,

Yisrael

Perfect, thank you very much!!!

Thanks Yisrael. I am attempting to remove geolocation and also adding this feature to another field, any tips?

also, how would you remove postcode from the results? Just want eg Brisbane, QLD, Australia.

Cheers!

@TailoredWebDesign

I generally don’t delete code till I’m sure. You can just comment it out to be sure that getting rid of the code doesn’t have any bad side effects. Just add // before the lines of code you want to “delete”. Then after you comment out all of the unneeded code and you’ve tested that everything works as it should, you can clean up and delete all of the commented out unneeded code.

It really shouldn’t be too difficult if you take it slow and easy - maybe with a beer in one hand :upside_down_face:

To get the desired string, you can use the results of the query and take only the fields that you want to construct the place string. Take a look at the Place Search docs for more information on this.