0

I have this project showing multiple locations using Google and Foursquare API. I was able to show the location and create a marker. My question is how do I show one info window at a time? If I click new location, the other location info window doesn't close. Right now if I click on one place and another. Both info window remains open.

My code is as follow. Thank you.

var initialLocations = [{
            name: 'Golden Gate',
            lat: 37.819929,
            long: -122.478255
        }, {
            name: 'AT&T Park',
            lat: 37.7749,
            long: -122.4194

        }, {
            name: 'Twin Peaks',
            lat: 37.7544,
            long: -122.4477
        },
    ];

var clientID = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
var clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";

var Location = function(data) {
    var self = this;

    this.name = data.name;
    this.lat = data.lat;
    this.long = data.long;
    this.URL = "";
    this.street = "";
    this.city = "";
    this.phone = "";

    this.visible = ko.observable(true);

    var foursquareURL = 'https://api.foursquare.com/v2/venues/search?ll=' + this.lat + ',' + this.long + '&client_id=' + clientID + '&client_secret=' + clientSecret + '&v=20160118' + '&query=' + this.name;

    $.getJSON(foursquareURL).done(function(data) {
        var results = data.response.venues[0];
        self.URL = results.url || 'No url found';
        self.street = results.location.formattedAddress[0];
        self.city = results.location.formattedAddress[1];
        self.phone = results.contact.phone  || 'No phone found';;

    }).fail(function() {
        alert("Error found. Please refresh your page.");
    });

    // Info window


    this.infoWindow = new google.maps.InfoWindow({


    });


    // Marker

    this.marker = new google.maps.Marker({
        position: new google.maps.LatLng(data.lat, data.long),
        map: map,
        title: data.name
    });


    this.marker.addListener('click', function() {

        self.contentString = '<div class="info-window-content"><div class="title"><b>' + data.name + "</b></div>" +
            '<div class="content"><a href="' + self.URL + '">' + self.URL + "</a></div>" +
            '<div class="content">' + self.street + "</div>" +
            '<div class="content">' + self.city + "</div>" +
            '<div class="content"><a href="tel:' + self.phone + '">' + self.phone + "</a></div></div>";

        self.infoWindow.setContent(self.contentString);

        self.infoWindow.open(map, this);

        self.marker.setAnimation(google.maps.Animation.BOUNCE);
        setTimeout(function() {
            self.marker.setAnimation(null);
        }, 2100);
    });

    this.bounce = function(place) {
        google.maps.event.trigger(self.marker, 'click');
    };
};


//-ViewModel
function AppViewModel() {
    var self = this;

    // create array of places

    this.locationList = ko.observableArray([]);
    this.searchLocation = ko.observable('');

    map = new google.maps.Map(document.getElementById('map'), {
        zoom: 12,
        center: {
            lat: 37.77986,
            lng: -122.429
        }
    });

    initialLocations.forEach(function(locationItem) {
        self.locationList.push(new Location(locationItem));
    });

    this.filteredList = ko.computed(function() {
        var filter = self.searchLocation().toLowerCase();
        if (!filter) {
            self.locationList().forEach(function(locationItem) {
                locationItem.visible(true);
            });
            return self.locationList();
        } else {
            return ko.utils.arrayFilter(self.locationList(), function(locationItem) {
                var string = locationItem.name.toLowerCase();
                var result = (string.search(filter) >= 0);
                locationItem.visible(result);
                return result;
            });
        }
    }, self);

    this.mapElem = document.getElementById('map');
    this.mapElem.style.height = window.innerHeight - 50;
}
// Bind the VeiewModel to the view using knockout
function startApp() {
    ko.applyBindings(new AppViewModel());
}

function errorHandling() {
    alert("Error loading page, try refresh in page.");
}
kyam po
  • 15
  • 4

2 Answers2

0
  1. You have to create a global variable where to save the infoWindow that is being displayed:

    var infowindow = null;

  2. Every time you go to show an infowindow you check if this is not null and you close it.

    if (infowindow) { infowindow.close(); }

  3. Then create a new instance and assign it to the variable:

    infowindow = new google.maps.InfoWindow (); ...

If you have questions, write me and I'll help you.

excuse my English

Here you can find more answers: Close all infowindows in Google Maps API v3

Samack77
  • 66
  • 1
  • 4
  • Thank you so much for your response. I tried to do as you suggested and it didn't work. Can you help? Thanks for your time. – kyam po Jan 23 '18 at 08:28
  • Is better to avoid global variables. You can create the infoWindow in the ViewModel and pass it to the Location objects. :-) – Jose Luis Jan 23 '18 at 10:26
0

You could share the infoWindow with all your Location objects. You need to move the infoWindow from Location to AppViewModel in order to have only an instance of this object.

Then, the AppViewModel should be:

//-ViewModel
function AppViewModel() {
    var self = this;

    // create array of places

    this.locationList = ko.observableArray([]);
    this.searchLocation = ko.observable('');

    var map = new google.maps.Map(document.getElementById('map'), {
        zoom: 12,
        center: {
            lat: 37.77986,
            lng: -122.429
        }
    });

    // ** Move the infoWindow from Location to AppViewModel.    
    var infoWindow = new google.maps.InfoWindow({
    });


    initialLocations.forEach(function(locationItem) {
        // To each Location object, pass the same infoWindow object
        self.locationList.push(new Location(locationItem, infoWindow, map));
    });

    // from here, the same code you have in AppViewModel
    ...
}

Now Add a parameter to the Location function (object):

function Location(data, infoWindow, map) {

Comment the infoWindow(or delete it, because you have it as a parameter):

    //// Info window
    //this.infoWindow = new google.maps.InfoWindow({
    //});

And the change the click listener:

    this.marker.addListener('click', function() {

        self.contentString = '<div class="info-window-content"><div class="title"><b>' + data.name + "</b></div>" +
            '<div class="content"><a href="' + self.URL + '">' + self.URL + "</a></div>" +
            '<div class="content">' + self.street + "</div>" +
            '<div class="content">' + self.city + "</div>" +
            '<div class="content"><a href="tel:' + self.phone + '">' + self.phone + "</a></div></div>";

        // ** Here, remove the 'self' in order to use the infoWindow received as parameter.
        infoWindow.setContent(self.contentString);

        // ** Here, remove the 'self' in order to use the infoWindow received as parameter.
        infoWindow.open(map, this);

        self.marker.setAnimation(google.maps.Animation.BOUNCE);
        setTimeout(function() {
            self.marker.setAnimation(null);
        }, 2100);
    });

This way all your Location objects will see the same infoWindow object. There is going to be only one infoWindow.

Update As for comments, I see that map becomes a global variable. It is declared in AppViewModel() but it should be declared with var or let. I changed its definition.

Then, we should add a new parameter to the Location object, the map, because now is not a globlal variable.

Jose Luis
  • 994
  • 1
  • 8
  • 22
  • Thank you so much, this works perfectly. However // ** Move the infoWindow from Location to AppViewModel. self.infoWindow = new google.maps.InfoWindow({ }); need to be just infoWindow = new google.maps.InfoWindow({ }); – kyam po Jan 23 '18 at 23:12
  • Then `infoWindow` becomes a global variable. You need to declare it with `var` or `let`: `var infoWindow = new google.maps.InfoWindow({ });`. The `map` variable should be `var map`. – Jose Luis Jan 24 '18 at 08:10
  • Please, have a look to this link: https://www.w3schools.com/js/js_scope.asp. "If you assign a value to a variable that has not been declared, it will automatically become a GLOBAL variable.". – Jose Luis Jan 24 '18 at 08:11