3
votes

VueJS version 1.9.0

app.js

require('./bootstrap');
window.Vue = require('vue');

Vue.component('mapbox', require('./components/mapbox.js'));

const app = new Vue({
    el: '#app'
});

components/mapbox.js

// initially from https://github.com/phegman/vue-mapbox-gl/blob/master/src/components/Mapbox.vue
export default {
    data() {
        return {
            _map: null
        };
    },
    props: {
        accessToken: {
            type: String,
            required: true
        },
        mapOptions: {
            type: Object,
            required: true
        },
        navControl: {
            type: Object,
            default: () => {
                return {
                    show: true,
                    position: 'top-right'
                };
            }
        },
        geolocateControl: {
            type: Object,
            default: () => {
                return {
                    show: false,
                    position: 'top-left',
                    options: {}
                };
            }
        },
        scaleControl: {
            type: Object,
            default: () => {
                return {
                    show: false,
                    position: 'top-left',
                    options: {}
                };
            }
        },
        fullscreenControl: {
            type: Object,
            default: () => {
                return {
                    show: false,
                    position: 'top-right'
                };
            }
        }
    },
    mounted() {
        const map = this.mapInit();
        this._map = map;
        this.registerEvents(map);
    },
    methods: {
        mapClicked(map, e) {
            console.log("clicked");
        },
        mapInit() {
            mapboxgl.accessToken = this.accessToken;
            if (!this.mapOptions.hasOwnProperty('container')) {
                this.mapOptions.container = 'map';
            }
            const map = new mapboxgl.Map(this.mapOptions);
            this.$emit('map-init', map);
            return map;
        },
        registerEvents(map) {
            map.on('load', () => {
                this.$emit('map-load', map);
            });
            map.on('click', e => {
                this.$emit('map-click', map, e);
            });
            map.on('render', () => {
                this.$emit('map-render', map);
            });
        }
    },
    beforeDestroy() {
        this._map.remove();
    }
};

index.blade.php

<mapbox 
    access-token="MY-ACCESS-TOKEN"

    :map-options="{
      style: 'mapbox://styles/mapbox/light-v9',
      center: [-96, 37.8],
      zoom: 3
    }"
    :geolocate-control="{
      show: true,
      position: 'top-left'
    }"
    :scale-control="{
      show: true,
      position: 'top-left'
    }"
    :fullscreen-control="{
      show: true,
      position: 'top-left'
    }"

    @map-click="mapClicked"

    inline-template>

        <div id="map"></div>

</mapbox>

From index.blade.php I'm trying to call @map-click="mapClicked" which is in components/mapbox.js's methods

But I'm getting the errors

▶ app.js:36934 [Vue warn]: Property or method "mapClicked" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

▶ [Vue warn]: Invalid handler for event "map-click": got undefined

  1. I know if I move the mapClicked to app.js: const app = new Vue({el: '#app', methods: {mapClicked(){..}}}); It will works but I want my file clean and would try to avoid this solution.
  2. I read this: Vue inline template not finding methods or data but it did not work for me.
1
Why not just call mapClicked in the map.on('click', ... handler? - thanksd
To be honest I didn't even think of that... (I will say as excuse I didn't use vueJs since 1 year :sweet_smile:) But If there's a way, I would still be happy to know for my knowledge or if someone stumble across this problem. - Clément Baconnier

1 Answers

1
votes

When you add the inline-template attribute to a component's tag, everything within the component's tag is used as the component's template and scoped to the component's Vue instance. However, the tag itself is still scoped to the parent. So the warning is telling you, as expected, that the parent component does not have a mapClicked method.

You should simply call the mapClicked method in the click handler that you set up in the registerEvents method:

map.on('click', e => {
  this.mapClicked(map, e);
  this.$emit('map-click', map, e);
});