Vue Set Data on Mapbox Click - vue.js

I'm using Mapbox to drop a marker on a map click. I successfully get the coordinates however I can't bind them to my data...
map.on('click', function(e) {
if (this.marker) { this.marker.remove() }
this.marker = new mapboxgl.Marker()
.setLngLat({ lng: e.lngLat.lng, lat: e.lngLat.lat})
.addTo(map);
map.flyTo({
center: { lng: e.lngLat.lng, lat: e.lngLat.lat },
zoom: 15
});
// This does not bind and update the data
this.latitude = JSON.stringify(e.lngLat.lat)
this.longitude = JSON.stringify(e.lngLat.lng)
})

It's a contextual binding issue. the this here does not refer to your vue instance but the map instead.
// fat arrow solves this
map.on('click', function(e) => {
})
// aliasing solves this
const self = this
map.on('click', function(e) {
})
// binding solves this
map.on('click', function(e) => {
}.bind(this))

Related

Vue JS Google Places API - Map not showing

Maps were not showing on the right after following the instructions step by step. The google api map itself is not showing.
(The API key is already in the script tag in my code, but I placed a placeholder instead here.)
Here is the video reference:
https://youtu.be/ID-_D0zJlSM
<div class="ten wide column segment ui m-0" ref="map"></div>
methods: {
locatorButtonPressed() {
navigator.geolocation.getCurrentPosition(
position => {
this.lat = position.coords.latitude;
this.lng = position.coords.longitude;
},
error => {
console.log("Error getting location");
}
);
},
findCloseByButtonPressed() {
const URL = `https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api/place/nearbysearch/json?
location=${this.lat},${this.lng}
&type=${this.type}
&radius=${this.radius * 1000}
&key=`[KEY HERE]`;
axios
.get(URL)
.then(response => {
this.places = response.data.results;
this.addLocationsToGoogleMaps();
})
.catch(error => {
console.log(error.message);
});
},
addLocationsToGoogleMaps() {
var map = new google.maps.Map(this.$refs['map'], {
zoom: 15,
center: new google.maps.LatLng(this.lat, this.lng),
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var infowindow = new google.maps.InfoWindow();
this.places.forEach(place => {
const lat = place.geometry.location.lat;
const lng = place.geometry.location.lng;
let marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map: map
});
google.maps.event.addListener(marker, "click", () => {
infowindow.setContent(
`<div class="ui header">${place.name}</div><p>${place.vicinity}</p>`
);
infowindow.open(map, marker);
});
});
}
}
}
if you want to implement map i prefer use this
click here

How do i update my mapbox markers when the geojson data is passed in as props in a vue component

Edit 1
Here is the source code on git
I am working on an Ionic-Vue project with Mapbox integration. My issue is that I am unable to update my geojson data source to create markers on the map, the geoJson data is passed in as props. Here is the flow
On APP.vue created hook
Using capacitor gets user current location.
.then() make an Axios call to the server to fetch the JSON data
On success set the data to store via mutation
On Home.vue[parent component for TheMap.vue]
Computed value called getGeoJson which calls the getters to get geojson data from the state saved an earlier step
Bind the :marker prop with computed value getGeoJson data on the TheMap component to be sent as a prop.
Listen to the event $updatedLocation and call the API action to fetch new geojson data.
On TheMap.vue
On create Hook call the createMap().
In Method: createMap()
Get the user coords again using capacitorJS
init the mapbox map and save it to this.map variable
add Attribution using this.map.addControl
this.map.on("load",cb) inside of cb call .addSource() & then addMarkers()
Create a new marker for the current user and save it to cosnt UserLocationMarker
UserLocationMarker.on("dragend",CB) to emit a event with latest current user location
Here is the code for the same just putting the script tags rather than the whole.vue file
APP.Vue
export default {
name: "App",
data() {
return {
currentLocation: "",
};
},
created() {
Geolocation.getCurrentPosition()
.then((result) => {
this.currentLocation = [
result.coords.longitude,
result.coords.latitude,
];
this.$store.dispatch(
`lists/${POST_GEOJSON_ACTION}`,
this.currentLocation
);
})
.catch((err) => console.log(err));
},
};
Home.vue
<template>
<the-map :markers="geojson" #updatedLocation="updateMap"></the-map>
</template>
<script>
export default {
name: "Home",
computed: {
getGeoJson() {
return this.$store.getters[`lists/${GET_GEOJSON_GETTER}`];
},
},
methods: {
updateMap(center) {
this.$store.dispatch(`lists/${POST_GEOJSON_ACTION}`, center);
},
},
};
</script>
TheMap.vue
export default {
props: ["markers"],
emits: ["updatedLocation"],
data() {
return {
access_token: process.env.VUE_APP_MAP_ACCESS_TOKEN,
center: [0, 0],
map: {},
};
},
mounted() {
this.createMap();
},
methods: {
async createMap() {
try {
const coords = await Geolocation.getCurrentPosition();
this.center = [coords.coords.longitude, coords.coords.latitude];
mapboxgl.accessToken = this.access_token;
//Map Instance
this.map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/userName/API_KEY",
center: this.center,
zoom: 12,
scrollZoom: true,
});
// Custom Attribution over the map for Branding
this.map.addControl(
new mapboxgl.AttributionControl({
customAttribution: ` © Comapny Name`,
})
);
this.map.on("load", function(e) {
this.map.addSource("places", {
type: "geojson",
data: this.markers,
});
this.addMarkers();
});
const UserLocationMarker = new mapboxgl.Marker({
draggable: true,
})
.setLngLat(this.center)
.addTo(this.map);
UserLocationMarker.on("dragend", async (e) => {
this.center = Object.values(e.target.getLngLat());
this.$emit("updatedLocation", this.center);
});
} catch (err) {
console.log(err);
}
},
addMarkers() {
this.markers.features.forEach(function(marker) {
var el = document.createElement("div");
el.id = "marker-" + marker.properties.id;
el.className = "marker";
new mapboxgl.Marker(el, { offset: [0, -23] })
.setLngLat(marker.geometry.coordinates)
.addTo(this.map);
});
},
},
};
My issue here is that the TheMap.vue does get undefined | [{geojson}] as a prop however it does not load the marker on init or even after the source is changed in the parent component.
What I expect is that the map on Load uses markers prop to build a list of markers if available else show nothing[handle undefined | null`] And update that marker list if a new set of Data is injected as prop on changed location.

How to use vuex with Konva for onDragEnd option

I am using konva with vuex together.
This is a code at '~.vue' for defining image.
There are two options onDragEnd and onTransform in "const yo".
'this.msg' and 'this.msg2' for the two options is defined in methods.
Thus, I can use the two options on realtime.
/gallery.vue
.
.
created() {
const image = new window.Image();
image.src = this.imageUpload.url;
image.onload = () => {
const yo = {
image: image,
name: "yoyo",
draggable: true,
scaleX: this.imageUpload.positions.scaleX,
scaleY: this.imageUpload.positions.scaleY,
x: this.imageUpload.positions._lastPosX,
y: this.imageUpload.positions._lastPosY,
onDragEnd: this.msg,
onTransform: this.msg2
};
this.images.push(yo);
};
},
methods: {
msg(e) {
this.savePositions(e.target.attrs);
},
msg2(e) {
this.savePositions(e.currentTarget.attrs);
},
But I want to move the code inside of 'created()' into 'vuex store' to control by one file.
Therefore, I make that in vuex store again like below.
And when I call this actions into 'gallery.vue', everything works well except the two options function as 'this.msg' and 'this.msg2'.
I guessed the problem would happen from 'e' argument. And I edited with various methods.
But that functions doesn;t work saying this.msg and this.msg2 is not function.
How can I call this function correctly?
Thank you so much for your reading.
/store.js
.
.
const actions = {
bringImage({ commit }) {
axios
.get(`http://localhost:4000/work`)
.then(payload => {
commit('pushWorks', payload);
})
.then(() => {
const image = new window.Image();
image.src = state.url;
image.onload = () => {
// set image only when it is loaded
const yo = {
image: image,
name: state.title,
draggable: true,
scaleX: state.positions.scaleX,
scaleY: state.positions.scaleY,
x: state.positions._lastPosX,
y: state.positions._lastPosY,
onDragEnd: this.msg,
onTransform: this.msg2
};
state.images.push(yo);
};
});
},
msg({ commit }, e) {
commit('savePositions', e.target.attrs);
},
msg2({ commit }, e) {
commit('savePositions', e.currentTarget.attrs);
}
}
You don't have this in your actions. So try to dispatch your actions with e argument as a payload.
bringImage({
commit,
dispatch
}) {
axios
.get(`http://localhost:4000/work`)
.then(payload => {
commit('pushWorks', payload)
})
.then(() => {
const image = new window.Image()
image.src = state.url
image.onload = () => {
// set image only when it is loaded
const yo = {
image: image,
name: state.title,
draggable: true,
scaleX: state.positions.scaleX,
scaleY: state.positions.scaleY,
x: state.positions._lastPosX,
y: state.positions._lastPosY,
onDragEnd: e => dispatch('msg', e),
onTransform: e => dispatch('msg2', e),
}
state.images.push(yo)
}
})
}

use vanilla-lazyload with vuejs

I'm pretty new in Vue-js and I'm trying to use this.
My App is not a SPA and I'm also working with Laravel.
I've tried this and it works fine:
const app = new Vue({
el: '#app',
mounted() {
this.myLazyLoad = new LazyLoad({
elements_selector: '.lazy',
class_loaded: 'lazy-loaded',
load_delay: 500, //adjust according to use case
threshold: 100, //adjust according to use case,
callback_enter: function(el) {
console.log(el.getAttribute("data-src"));
}
});
}
});
<img data-src="{{ $featuredItem->anime->getPortraitImg()}}"
class="lazy img-fluid" src="/img/placeholder-anime.jpg">
But there is a problem when I try to use the lazyload in a Component.
For example:
export default {
mounted() {
console.log('Component mounted.');
console.log(Vue.prototype.LazyLoad);
this.myLazyLoad = new LazyLoad({
// elements_selector: '.lazyx',
container: document.getElementById('lazyContainer')
});
// myLazyLoad.loadAll();
},
data() {
return {
episodes: {},
currentPage: 2
}
},
methods: {
loadMoreEpisodes() {
let uri = '/api/v1/episodes?page=' + this.currentPage;
this.axios.get(uri).then(response => {
console.log(response.data.data);
if (this.episodes.length === undefined) {
this.episodes = response.data.data;
} else {
this.episodes = this.episodes.concat(response.data.data);
}
this.myLazyLoad.update();
this.myLazyLoad.loadAll();
});
}
}
}
The new data inserted by axios is not recognized by the lazyload plugin.
I'm using this.myLazyLoad.update(); as stated in the documentation, but I'm not able to get it to work. Any suggestions?
I think DOM is not updated when you call update() method. Can you try using $nextTick?
this.$nextTick(() => {
this.myLazyLoad.update()
})

Automatically closing Drawer when view width is decreasing

I'm trying to make a view like Firebase's console with React and material-ui.
How can I build a Drawer that will automatically close when view(browser) width is decreasing.
Quite easy, you can hook up the listener on resize event on your react class:
var RootPage = React.createClass({
render: function() {
return <Drawer refs={'drawer'} />;
},
// we trigger our drawer here
componentWillUpdate: function(nextProp, nextState) {
if(nextState.width < this.state.width) {
this.refs.drawer.open = false;
}
},
windowOnResize: function() {
var width = $(window).width();
this.setState({ width: width });
},
componentWillMount: function() {
this.windowOnResize();
},
componentDidMount: function() {
window.addEventListener("resize", this.windowOnResize);
},
componentWillUnmount: function() {
window.removeEventListener("resize", this.windowOnResize);
}
});