Vue google maps infowindow custom buttons not working

3.6k views Asked by At

Hey vue-ers!

I am using vue2-google-maps with all its glories and so far I have managed a lot except for custom buttons within an info-window. So far I can click on a marker and it shows the related custom info-window with the right info. The only thing I am missing now is making those buttons work. For example the close button: I can click on it and it speaks to the component method but does not close it. The close button works the same as selecting the markers, but at the same time it's not? The values update to false, but not being updated in the maps, while I select a marker it updates like a charm. So I am quite stuck here and fairly confused, any help, ideas and tips are appreciated!

Here is my code for reference:

Template

<gmap-map
    id="map"
    :center="center"
    :zoom=13
    :options="{
        disableDefaultUI: true,
        zoomControl: true,
        clickableIcons: true,
    }">

    <gmap-marker
        v-for="(location, index) in locations"
        :position="{lat: location.latitude, lng: location.longitude}"
        :icon="location.icon"
        :zone="location.zone"
        :jettyNumber="location.jettyNumber"
        :selected="location.selected"
        :zIndex="location.zIndex"
        @mouseover="mouseOverMarker(index)"
        @mouseout="mouseOutMarker(index)"
        @click="openInfoWindow(index)"
        :label="{
            text: location.jettyNumber.toString(),
            color: '#fff',
            fontSize: '13px',
            fontFamily: 'din_round_otbold'
        }">

        <gmap-info-window
            :opened="location.infoBoxOpen">
                <div class="infoWindow" style="width: 300px;">
                    <div id="dock-zone" :class="getZoneClass(location.zone)">{{ getZoneClassText(location.zone) }}</div>
                    <h2 id="infoWindow-location">{{ location.name }}</h2>
                    <div id="close-btn" @click="closeInfoWindow(index)"></div>
                    <a class="btn btn-primary google-maps-infowindow-button">Kies als <b>{{ inputData.label }}</b></a>
                    <span></span>
                </div>
        </gmap-info-window>
    </gmap-marker>
</gmap-map>

Component Script

import $ from "jquery";
export default {
data: function(){
    return {
        icons: {
            stopInner: {
                icon: process.env.VUE_APP_PUBLIC_URL + '/Assets/images/pinpoint_on_map_inner.svg'
            },
            stopOuter: {
                icon: process.env.VUE_APP_PUBLIC_URL + '/Assets/images/pinpoint_on_map_outer.svg'
            },
            stopOutside: {
                icon: process.env.VUE_APP_PUBLIC_URL + '/Assets/images/pinpoint_on_map_outside.svg'
            },
            stopSelected: {
                icon: process.env.VUE_APP_PUBLIC_URL + '/Assets/images/pinpoint_on_map_active.svg'
            }
        },
        locations: [],
        center: {lat: 51.9004, lng: 4.46849},
        inputData: {}
    };
},
methods: {
    initMap(locations, inputData) {
        // Set maps specific data
        for(var i = 0; i < locations.length; i++) {
            locations[i].selected = false;
            locations[i].infoBoxOpen = false;
            locations[i].zIndex = locations[i].jettyNumber;
            locations[i].icon = this.getIcon(locations[i].zone);
        }
        this.inputData = inputData;
        this.locations = locations;
    },
    getIcon(zone) {
        switch(zone){
            case "centre":
                return this.icons.stopInner.icon;
            break;
            case null:
                return this.icons.stopOutside.icon;
            break;
            default:
                return this.icons.stopOuter.icon;
            break;
        }
    },
    getZoneClass(zone) {
        switch(zone){
            case "centre":
                return "centrum";
            break;
            case null:
                return "buiten";
            break;
            default:
                return "";
            break;
        }
    },
    getZoneClassText(zone) {
        if(zone){
            return zone;
        }else{
            return "outside";
        }
    },
    // TODO: Not really setting the zindex, need to dig deeper
    mouseOverMarker(locationIndex) { 
        this.locations[locationIndex].zIndex = 99998;
    },
    mouseOutMarker(locationIndex) { 
        this.locations[locationIndex].zIndex = this.locations[locationIndex].jettyNumber; 
    },
    openInfoWindow(location){
        this.center = {lat: location.latitude, lng: location.longitude};
        this.setSelectedLocation(location);
    },
    setSelectedLocation(selectedLocationIndex){
        this.unselectLocations();
        // Set selected
        this.locations[selectedLocationIndex].zIndex = 88888;
        this.locations[selectedLocationIndex].icon = this.icons.stopSelected.icon;
        this.locations[selectedLocationIndex].selected = true;
        this.locations[selectedLocationIndex].infoBoxOpen = true;
        this.center = {
            lat: this.locations[selectedLocationIndex].latitude,
            lng: this.locations[selectedLocationIndex].longitude
        };
        this.alterDefaultInfoWindowStyle();
    },
    unselectLocations() {
        for(var i = 0; i < this.locations.length; i++) {
            this.locations[i].icon = this.getIcon(this.locations[i].zone);
            this.locations[i].zIndex = this.locations[i].jettyNumber;
            this.locations[i].selected = false;
            this.locations[i].infoBoxOpen = false;
        }
    },
    alterDefaultInfoWindowStyle() {
        // TODO: a very nasty fix, need a better way to handle this
        setTimeout(function(){
            var iwOuter = $('.gm-style-iw');
            iwOuter.parent().parent().css({ top: '20px' });
            iwOuter.css({ top: '0px' });
            iwOuter.css({ left: '26px' });

            var iwBackground = iwOuter.prev();
            iwBackground.css({ 'display': 'none' });
            var iwCloseBtn = iwOuter.next();

            iwCloseBtn.hide();
            iwOuter.children().eq(0).css({ overflow: 'visible' });
        }, 10);
    },
    closeInfoWindow(selectedLocationIndex) {
        this.unselectLocations();
    },
    closeMaps() {
        this.$emit('close-maps')
    }
}

}

1

There are 1 answers

0
Vadim Gremyachev On

Since the appearance of info window is determined by locations[index].infoBoxOpen the standard close handler (@closeclick) needs to be implemented as well, like this:

 <gmap-info-window
        :opened="location.infoBoxOpen"
        @closeclick="location.infoBoxOpen=false">
    ...
 </gmap-info-window>  

I would also propose to modify the way how info window is created. Instead of creating info window per marker, to create only a single instance of info window. In addition to performance benefits it would make it more clear to manage info window state, for example:

<gmap-map :center="center" :zoom="zoom" ref="map">
  <gmap-marker
    :key="index"
    v-for="(location,index) in locations"
    :position="{lat: location.lat,lng:location.lng}"
    :clickable="true"
    @click="openInfoWindow(location)"
  />
  <gmap-info-window v-if="selectedLocation !== null" :position="{lat: selectedLocation.lat,lng:selectedLocation.lng}" :opened="infoBoxOpen" @closeclick="closeInfoWindow()">
      <div class="infoWindow">
        <h2 id="infoWindow-location">{{ selectedLocation.name }}</h2>
        <button @click="closeInfoWindow()">Close</button>
      </div>
    </gmap-info-window>
</gmap-map>

export default {
  data: () => ({
    zoom: 5,
    center: { lat: 59.339025, lng: 18.065818 },
    selectedLocation: null,
    infoBoxOpen: false,
    locations: [
      {
        Id: 1,
        name: "Oslo",
        lat: 59.923043,
        lng: 10.752839
      },
      {
        Id: 2,
        name: "Stockholm",
        lat: 59.339025,
        lng: 18.065818
      },
      {
        Id: 3,
        name: "Copenhagen",
        lat: 55.675507,
        lng: 12.574227
      },
      {
        Id: 4,
        name: "Berlin",
        lat: 52.521248,
        lng: 13.399038
      },
      {
        Id: 5,
        name: "Paris",
        lat: 48.856127,
        lng: 2.346525
      }
    ]
  }),
  methods: {
    openInfoWindow(location) {
      this.selectedLocation = location
      this.infoBoxOpen = true;
    },
    closeInfoWindow() {
      this.infoBoxOpen = false;
    }
  }
};    

Here is a demo that demonstrates how to manage info window per multiple markers