I am trying to build a map application with the vuemapbox library , but the strange thing is , all the markers are showing at the bottom of the page which is strange , can any one help me with it ?
<template>
<div class="home">
<MglMap :accessToken="accessToken" :mapStyle="mapStyle" #load="onMapLoaded">
<MglMarker
:coordinates.sync="locations"
color="blue"
v-for="(location, index) in pins"
:key="index"
/>
</MglMap>
</div>
</template>
<script>
// # is an alias to /src
import Mapbox from "mapbox-gl";
import {
MglMap,
MglMarker
} from "vue-mapbox";
export default {
name: "Home",
components: { MglMap, MglMarker },
data() {
return {
accessToken:"xxx",
mapStyle: "mapbox://styles/mapbox/streets-v11",
pins: [
[20.341979525348204, 85.8345150468384],
[25.581627613058714, 80.87795332144299],
[25.199370930176993, 95.86881932189225],
],
};
},
beforeMount() {
this.$store.dispatch("set_user_location");
},
computed: {
locations() {
return this.$store.getters.getThisUserMarker;
},
},
created() {
this.mapbox = Mapbox;
},
methods: {
async onMapLoaded(event) {
// in component
this.map = event.map;
const asyncActions = event.component.actions;
await asyncActions.flyTo({
center: this.locations,
zoom: 4,
speed: 1,
});
},
},
};
</script>
This is what i have done so far , but having this strange issue , please help , Every help is appreciated .
Make shure that you have loaded mabpox css.
Try to set height for MglMap
<MglMap
width="100%"
height="100%"
...
/>
Related
How can I solve this Cannot read property 'maps' of null", when I call initMap to center the map with this line of code,
const map = new this.google.maps.Map(document.getElementById('map'), {
zoom: 13,
center: this.property
})
I already define this.google in compute also, but it doesn't work.
This is what i have done
<template>
<q-layout view="hhh lpr fff" class="main-layout">
<q-page-container>
<GmapMap
id="map"
ref="mapRef"
:center="{lat:-7.250445, lng:112.768845}"
:zoom="13"
map-type-id="terrain"
style="width: 1000px; height: 600px; margin-left:100px"
>
</GmapMap>
<router-view />
</q-page-container>
</q-layout>
</template>
<script>
import * as VueGoogleMaps from 'vue2-google-maps'
export default {
name: 'DefaultLayout',
data () {
return {
mapName: "map",
property: {
lat: 1.28237,
lng: 103.783098
},
}
},
computed: {
google: VueGoogleMaps.gmapApi
},
mounted () {
this.initMap();
},
methods: {
initMap() {
const map = new this.google.maps.Map(document.getElementById('map'), {
zoom: 13,
center: this.property
})
},
},
}
</script>
<style>
</style>
Anyone can help me solve this problem?
Computed property should be a getter function. I think you don't need a computed property here. You can make everything in the initMap method, e.g.
initMap() {
if(VueGoogleMaps) {
new VueGoogleMaps.gmapApi.maps.Map(document.getElementById('map'), {
zoom: 13,
center: this.property
})
}
}
I'm learning Vue.js and have been able to write a simple list/detail application. Selecting the first item renders the detail component with the correct data, however when I select a different item the detail component doesn't reload with the right information.
For example:
<template>
<div>
<detail :obj="currentObject"></detail>
</div>
</template>
<script>
export default: {
data: function(){
return {
currentObject: null,
objs = [
{name:'obj 1'},
{name:'obj 2'}
]
};
}
}
</script>
When I do this.currentObject = objs[0] the component detail updates with the correct content. However the next time I call this.currentObject = objs[1], the component detail no longer updates.
Not sure what's the context your are switching the data on your currentObject, but the below is a concept detail component and when you switch the objs it updated the prop :obj and seems working fine.
Looking at your code, you should declare objs using : not =.
data: function() {
return {
currentObject: null,
objs: [
{name:'obj 1'},
{name:'obj 2'}
]
};
}
Here is the concept detail component, run the snippet to check it working.
Vue.component('detail', {
props: ['obj'],
template: '<div>{{ obj }}</div>'
})
var app = new Vue({
el: '#app',
data() {
return {
bToggle: false,
currentObject: null,
objs: [
{name:'obj 1'},
{name:'obj 2'}
]
}
},
created(){
this.switchData();
},
methods: {
switchData() {
if(!this.bToggle){
this.currentObject = this.objs[0];
}else{
this.currentObject = this.objs[1];
}
this.bToggle = !this.bToggle;
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<button #click="switchData()"> switch </button>
<detail :obj="currentObject"></detail>
</div>
I read Reactivity in Depth but can't solve the issue.
I'm creating a small single page app that contains images and text.
When the user clicks a button I want the language to change.
Currently I am storing the content in two files that export an object.
export default {
projects: [
{
title: 'Project Title',
year: 2016,
...
},
]
}
and importing that
import contentEn from './assets/content.en.js'
import contentDe from './assets/content.de.js'
new Vue({
el: '#app',
components: { App },
data: {
mainContent: {
content: contentEn
}
},
methods: {
switchToGerman(){
this.mainContent.content = contentDe
}
},
template: '<App :mainData="mainContent"/>',
})
When I assign another object to mainContent.content the rendering is not triggered.
I understand that adding and deleting properties from object don't lead to change detection but I switch out a whole object. I tried assigning it with this.$set with no success.
I also tried this and googled a lot but can't get it work.
Or is my approach just wrong?
Thank you for helping,
best,
ccarstens
EDIT:
See below the code for the App component and the ProjectElement
// App.vue
<template>
<div id="app">
<button #click="switchGerman">Deutsch</button>
<ProjectElement v-for="(project, key) in fullData.content.projects" :key="key" :content="project"/>
</div>
</template>
<script>
import ProjectElement from './components/ProjectElement'
export default {
name: 'App',
props: [
'mainData'
],
data () {
return{
fullData: {}
}
},
methods: {
switchGerman(){
this.$root.switchToGerman()
}
},
created(){
this.fullData = this.$props.mainData
},
watch: {
mainData: {
handler: function(newData){
this.fullData = newData
},
deep: true
}
},
components: {
ProjectElement,
}
}
</script>
And the ProjectElement
//ProjectElement.vue
<template>
<article :class="classObject" v-observe-visibility="{
callback: visibilityChanged,
throttle,
intersection: {
threshold
}
}">
<header v-html="description"></header>
<div class="content">
<carousel :per-page="1" :pagination-enabled="false">
<slide v-for="(slide, index) in projectContent.media" :key="index">
<VisualElement :content="slide" ></VisualElement>
</slide>
</carousel>
</div>
</article>
</template>
<script>
import {Carousel, Slide} from 'vue-carousel'
import VisualElement from './VisualElement'
export default {
name: "ProjectElement",
components: {
Carousel,
Slide,
VisualElement
},
props: [
'content'
],
data () {
return {
projectContent: {},
isVisible: false,
throttle: 300,
threshold: 0.8
}
},
created(){
this.projectContent = this.content
},
methods: {
visibilityChanged(isVisible){
this.isVisible = isVisible
}
},
computed: {
description(){
return `
<p>${ this.projectContent.title } - ${this.projectContent.year}</p>
<p>${ this.projectContent.description }</p>
`
},
classObject(){
return {
visible: this.isVisible,
'project-element': true
}
}
}
}
</script>
Did you try doing deep copy:
switchToGerman () {
const copyContent = JSON.parse(JSON.stringify(this.mainContent))
copyContent.content = contentDe
this.mainContent = copyContent
}
I found the solution (thank you #EricGuan for pointing out that the mistake must lay somewhere else)
As you can see in the original post I created a watcher for the mainData property and expected that this would trigger the re render.
What was missing is, that I didn't watch the content property on the ProjectElement component, thus not triggering a re render there.
I added this to ProjectElement.vue and now it works like a charm:
watch: {
content(newContent){
this.projectContent = newContent
}
},
Thank you everybody for helping me! <3
I am new to Vue and am trying to build a simple movie app, fetching data from an API and rendering the results. I want to have an incremental search feature. I have an input field in my navbar and when the user types, I want to redirect from the dashboard view to the search results view. I am unsure of how to pass the query params from the navbar to the search results view.
Here is my App.vue component
<template>
<div id="app">
<Navbar></Navbar>
<router-view/>
</div>
</template>
<script>
import Navbar from './components/Navbar.vue'
export default {
name: 'App',
components: {
Navbar
},
}
</script>
And here is my navbar component where I have the input field
<template>
<nav class="navbar">
<h1 class="logo" v-on:click="goToHome">Movie App</h1>
<input class="search-input" v-on:keyup="showResults" v-model="query" type="text" placeholder="Search..."/>
</nav>
</template>
<script>
import router from '../router/index'
export default {
data: function () {
return {
query: this.query
}
},
methods: {
goToHome () {
router.push({name: 'Dashboard'})
},
showResults () {
//here on each key press I want to narrow my results in the SearchedMovies component
}
}
}
</script>
If I use router.push to the SearchedMovies component then I am only able to pass the query as a parameter once. I thought about using Vuex to store the query and then access it from the SearchedMovies component, but surely there is a better way of doing it?
I also read about using $emit but since my parent contains all the routes, I'm not sure how to go about this.
You don't need to redirect user anywhere. I've made a small demo to show how one might do it. I used this navbar component as you described and emit an event from it:
const movies = {
data: [
{
id: 0,
title: 'Eraserhead',
},
{
id: 1,
title: 'Erazerhead',
},
{
id: 2,
title: 'Videodrome',
},
{
id: 3,
title: 'Videobrome',
},
{
id: 4,
title: 'Cube',
},
]
};
Vue.component('navbar', {
template: '<input v-model="filter" #input="onInput" placeholder="search">',
data() {
return {
filter: '',
};
},
methods: {
onInput() {
this.$emit('filter', this.filter);
}
}
});
// this is just a request imitation.
// just waiting for a second until we get a response
// from the datasample
function request(title) {
return new Promise((fulfill) => {
toReturn = movies.data.filter(movie => movie.title.toLowerCase().indexOf(title.toLowerCase()) !== -1)
setTimeout(() => fulfill(toReturn), 1000);
});
}
new Vue({
el: '#app',
data: {
movies: undefined,
loading: false,
filter: '',
lastValue: '',
},
methods: {
filterList(payload) {
// a timeout to prevent
// instant request on every input interaction
this.lastValue = payload;
setTimeout(() => this.makeRequest(), 1000);
},
makeRequest() {
if (this.loading) {
return;
}
this.loading = true;
request(this.lastValue).then((response) => {
this.movies = response;
this.loading = false;
});
}
},
mounted() {
this.makeRequest('');
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<navbar v-on:filter="filterList"></navbar>
<ul v-if="!loading">
<li v-for="movie in movies" :key="movie.id">{{ movie.title }}</li>
</ul>
<p v-else>Loading...</p>
</div>
Also jsfiddle: https://jsfiddle.net/oniondomes/rsyys3rp/
If you have any problem to understand the code above let me know.
EDIT: Fixed some bugs and added a couple of comments
EDIT2(after the comment below):
Here's what you can do. Every time user inputs something inside a navbar you call a function:
// template
<navbar v-on:input-inside-nav-bar="atInputInsideNavBar"></navbar>
// script
methods: {
atInputInsideNavBar(userInput) {
this.$router.push({
path: '/filtred-items',
params: {
value: userInput
}
})
}
}
Then inside you 'searched movies' page component you can access this value so:
this.$route.params.value // returns userInput from root component
I'm building a SPA with a scroll navigation being populated with menu items based on section components.
In my Home.vue I'm importing the scrollNav and the sections like this:
<template>
<div class="front-page">
<scroll-nav v-if="scrollNavShown" #select="changeSection" :active-section="activeItem" :items="sections"></scroll-nav>
<fp-sections #loaded="buildNav" :active="activeItem"></fp-sections>
</div>
</template>
<script>
import scrollNav from '.././components/scrollNav.vue'
import fpSections from './fpSections.vue'
export default {
data() {
return {
scrollNavShown: true,
activeItem: 'sectionOne',
scrollPosition: 0,
sections: []
}
},
methods: {
buildNav(sections) {
this.sections = sections;
console.log(this.sections)
},
changeSection(e) {
this.activeItem = e
},
},
components: {
scrollNav,
fpSections
}
}
</script>
this.sections is initially empty, since I'm populating this array with data from the individual sections in fpSections.vue:
<template>
<div class="fp-sections">
<keep-alive>
<transition
#enter="enter"
#leave="leave"
:css="false"
>
<component :is="activeSection"></component>
</transition>
</keep-alive>
</div>
</template>
<script>
import sectionOne from './sections/sectionOne.vue'
import sectionTwo from './sections/sectionTwo.vue'
import sectionThree from './sections/sectionThree.vue'
export default {
components: {
sectionOne,
sectionTwo,
sectionThree
},
props: {
active: String
},
data() {
return {
activeSection: this.active,
sections: []
}
},
mounted() {
this.buildNav();
},
methods: {
buildNav() {
let _components = this.$options.components;
for(let prop in _components) {
if(!_components[prop].hasOwnProperty('data')) continue;
this.sections.push({
title: _components[prop].data().title,
name: _components[prop].data().name
})
}
this.$emit('loaded', this.sections)
},
enter(el) {
twm.to(el, .2, {
autoAlpha : 1
})
},
leave(el, done) {
twm.to(el, .2, {
autoAlpha : 0
})
}
}
}
</script>
The buildNav method loops through the individual components' data and pushes it to a scoped this.sections array which are then emitted back to Home.vue
Back in Home.vue this.sections is populated with the data emitted from fpSections.vue and passed back to it as a prop.
When I inspect with Vue devtools the props are passed down correctly but the data does not update.
What am I missing here? The data should react to props when it is updated in the parent right?
:active="activeItem"
this is calld "dynamic prop" not dynamic data. You set in once "onInit".
For reactivity you can do
computed:{
activeSection(){ return this.active;}
}
or
watch: {
active(){
//do something
}
}
You could use the .sync modifier and then you need to emit the update, see my example on how it would work:
Vue.component('button-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
props: ['counter'],
watch: {
counter: function(){
this.$emit('update:counter',this.counter)
}
},
})
new Vue({
el: '#counter-sync-example',
data: {
foo: 0,
bar: 0
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="counter-sync-example">
<p>foo {{ foo }} <button-counter :counter="foo"></button-counter> (no sync)</p>
<p>bar {{ bar }} <button-counter :counter.sync="bar"></button-counter> (.sync)</p>
</div>