Pass array data to component and make - vue.js

I try to get familiar with Vue2.
As you can see below I try to pass in the "markers"-Array but the map does not change. How can I change the markers array? How can I make the markers array reactive so that map markers change?
Thanks for helping me!
views/Map.vue
<template>
<div id="MapWrapper">
<Map :markers = "markers"/>
</div>
</template>
<script>
import Map from 'src/components/MapSoal.vue'
export default {
name: 'MapView',
components: {
Map
},
data () {
return {
markers: [
{name: "greenMarker", lngLat: [13.8022177, 51.0069449], color: "green", text: "<h1>This is the green marker</h1>"},
{name: "orangeMarker", lngLat: [13.8022177 - 0.05, 51.0069449], color: "orange", text: "<h1>this is the orange marker</h1>"},
]
}
}
}
</script>
components/MapSoal.vue
<template>
<MglMap
:accessToken="accessToken"
:mapStyle="mapStyle"
:center="center"
:zoom="zoom"
>
<MglMarker v-for="marker in markers" :key="marker.name" :coordinates="marker.lngLat" :color="marker.color">
<MglPopup>
<VCard v-html="marker.text"></VCard>
</MglPopup>
</MglMarker>
<MglGeolocateControl></MglGeolocateControl>
<MglNavigationControl></MglnavigationControl>
</MglMap>
</template>
export default {
data() {
return {
accessToken: 'SOME_API_KEY',
mapStyle: 'mapbox://styles/mapbox/streets-v11?optimize=true',
center: [13.8022177, 51.0069449],
zoom: 9, // starting zoom
markers: []
};
},
}

You're not declaring markers as a prop in your Map component, you're declaring markers in your data object, so the Map component's got a separate data variable named markers, completely unrelated to your parent component's data. Try removing your markers array from data in your Map component and add it in a props object in the component like this:
props: {
markers: {
type: Array
}
},
or
props: ['markers'],
so that your Map component looks like this:
<template>
<MglMap
:accessToken="accessToken"
:mapStyle="mapStyle"
:center="center"
:zoom="zoom"
>
<MglMarker v-for="marker in markers" :key="marker.name" :coordinates="marker.lngLat" :color="marker.color">
<MglPopup>
<VCard v-html="marker.text"></VCard>
</MglPopup>
</MglMarker>
<MglGeolocateControl></MglGeolocateControl>
<MglNavigationControl></MglnavigationControl>
</MglMap>
</template>
export default {
props: ['markers'],
data() {
return {
accessToken: 'SOME_API_KEY',
mapStyle: 'mapbox://styles/mapbox/streets-v11?optimize=true',
center: [13.8022177, 51.0069449],
zoom: 9, // starting zoom
};
},
}

Related

How do have unique variables for each dynamically created buttons/text fields?

I'm trying to create buttons and vue element inputs for each item on the page. I'm iterating through the items and rendering them with v-for and so I decided to expand on that and do it for both the rest as well. The problem i'm having is that I need to to bind textInput as well as displayTextbox to each one and i'm not sure how to achieve that.
currently all the input text in the el-inputs are bound to the same variable, and clicking to display the inputs will display them all at once.
<template>
<div class="container">
<div v-for="(item, index) in items" :key="index">
<icon #click="showTextbox"/>
<el-input v-if="displayTextbox" v-model="textInput" />
<el-button v-if="displayTextbox" type="primary" #click="confirm" />
<ItemDisplay :data-id="item.id" />
</div>
</div>
</template>
<script>
import ItemDisplay from '#/components/ItemDisplay';
export default {
name: 'ItemList',
components: {
ItemDisplay,
},
props: {
items: {
type: Array,
required: true,
},
}
data() {
displayTextbox = false,
textInput = '',
},
methods: {
confirm() {
// todo send request here
this.displayTextbox = false;
},
showTextbox() {
this.displayTextbox = true;
}
}
}
</script>
EDIT: with the help of #kissu here's the updated and working version
<template>
<div class="container">
<div v-for="(item, index) in itemDataList" :key="itemDataList.id">
<icon #click="showTextbox(item.id)"/>
<El-Input v-if="item.displayTextbox" v-model="item.textInput" />
<El-Button v-if="item.displayTextbox" type="primary" #click="confirm(item.id)" />
<ItemDisplay :data-id="item.item.uuid" />
</div>
</div>
</template>
<script>
import ItemDisplay from '#/components/ItemDisplay';
export default {
name: 'ItemList',
components: {
ItemDisplay,
},
props: {
items: {
type: Array,
required: true,
},
}
data() {
itemDataList = [],
},
methods: {
confirm(id) {
const selected = this.itemDataList.find(
(item) => item.id === id,
)
selected.displayTextbox = false;
console.log(selected.textInput);
// todo send request here
},
showTextbox(id) {
this.itemDataList.find(
(item) => item.id === id,
).displayTextbox = true;
},
populateItemData() {
this.items.forEach((item, index) => {
this.itemDataList.push({
id: item.uuid + index,
displayTextbox: false,
textInput: '',
item: item,
});
});
}
},
created() {
// items prop is obtained from parent component vuex
// generate itemDataList before DOM is rendered so we can render it correctly
this.populateItemData();
},
}
</script>
[assuming you're using Vue2]
If you want to interact with multiple displayTextbox + textInput state, you will need to have an array of objects with a specific key tied to each one of them like in this example.
As of right now, you do have only 1 state for them all, meaning that as you can see: you can toggle it for all or none only.
You'll need to refactor it with an object as in my above example to allow a case-per-case iteration on each state individually.
PS: :key="index" is not a valid solution, you should never use the index of a v-for as explained here.
PS2: please follow the conventions in terms of component naming in your template.
Also, I'm not sure how deep you were planning to go with your components since we don't know the internals of <ItemDisplay :data-id="item.id" />.
But if you also want to manage the labels for each of your inputs, you can do that with nanoid, that way you will be able to have unique UUIDs for each one of your inputs, quite useful.
Use an array to store the values, like this:
<template>
<div v-for="(item, index) in items" :key="index">
<el-input v-model="textInputs[index]" />
</div>
<template>
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
},
data() {
textInputs: []
}
}
</script>

Vuemapbox markers are showing at the bottom of the page

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%"
...
/>

How to fix Cannot read property 'maps' of null" on Quasar Vue Google Maps?

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
})
}
}

Is possible to generate vue component using for loop inside function?

Is possible to generate vue component using for loop. I am trying to generate and able to get but it's override by new component dynamically it's override component one schema also with component second with is at last generated.
https://jsfiddle.net/3ordn7sj/5/
https://jsfiddle.net/bt5dhqtf/973/
for (var key in app.html) {
Vue.component(key, {
template: `<div><vue-form-generator :model="model"
:schema="schema"
:options="formOptions"
ref="key"></vue-form-generator>{{ key }}</div>`,
mounted() {
this.schema = app.html[key]
},
data: function () {
return {
schema: app.html[key],
key: '',
formOptions: this.formOptions,
model: this.model,
}
},
}
)
}
Is possible to generate vue component using for loop. I am trying to generate and able to get but it's override by new component dynamically it's override component one schema also with component second with is at last generated. In above jsfiddel link my data is there inside created.
I am trying to generate vue component base on this data and I am using vue form generator.In above code what exactly I am trying to do is while my loop running form generated but I don't know how it's first component also getting second component schema and it;s showing on first step also overrides schema data.
I am very confused why this is happening I tried a lot but I am not getting any solution if you have please suggest what I can do for generate component using for loop inside function.
Please try to solve this issue or tell me id it;s not possible.
I did like this
<form-wizard #on-complete="onComplete"
#on-change="handleChange"
validate-on-back
ref="wizard"
:start-index.sync="activeTabIndex"
shape="circle" color="#20a0ff" error-color="#ff4949" v-if="html != 0">
<tab-content v-for="tab in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<component :is="tab.component"></component>
</tab-content>
</form-wizard>
Inside Data I have added for now this tabs option
tabs: [{title: 'Personal details', icon: 'ti-user', component: 'firstTabSchema'},
{title: 'Is Logged In?', icon: 'ti-settings', component: 'secondTabSchema', hide: false},
],
generateNewForm.vue
<template>
<div class="app animated fadeIn">
<loading :active.sync="this.$store.state.isLoading"
:can-cancel="true"
:is-full-page="this.$store.state.fullPage"></loading>
<b-row>
<b-col cols="12" xl="12">
<transition name="slide">
<b-card>
<div slot="header">
<b-button variant="primary" #click="goBack"><i class="icon-arrow-left icons font-1xl"></i>Back</b-button>
</div>
<formcomponent :tree="this.$store.state.formData" />
</b-card>
</transition>
</b-col>
</b-row>
</div>
</template>
<script>
import {store} from '#/components/store'
import formcomponent from '#/components/formcomponent';
import Vue from 'vue'
import Loading from 'vue-loading-overlay';
import 'vue-loading-overlay/dist/vue-loading.css';
import {FormWizard, TabContent} from 'vue-form-wizard'
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
import VueFormGenerator from "vue-form-generator";
/*import VeeValidate from 'vee-validate';*/
Vue.use(VueFormGenerator);
Vue.use(Loading);
export default {
name: 'tables',
store: store,
data: () => {
return {
finalModel: {},
activeTabIndex: 0,
model: {},
count: 0,
}
},
components: {
'loading': Loading,
FormWizard,
TabContent,
formcomponent: formcomponent
},
created() {
},
beforeMount() {
this.$store.dispatch('loadFormData', this.$route.params.id);
},
methods: {
onComplete: function(){
alert('Yay. Done!');
},
goBack() {
this.$router.go(-1)
}
}
}
</script>
formcomponent.vue
<template>
<div>
<form-wizard #on-complete="onComplete"
#on-change="handleChange"
validate-on-back
ref="wizard"
:start-index.sync="activeTabIndex"
shape="circle" color="#20a0ff" error-color="#ff4949" v-if="html != 0">
<tab-content v-for="tab in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<component :is="tab.component"></component>
</tab-content>
</form-wizard>
</div>
</template>
<script>
import Vue from 'vue'
import {FormWizard, TabContent} from 'vue-form-wizard'
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
import VueFormGenerator from "vue-form-generator";
//console.log(Vue.options);
Vue.use(VueFormGenerator);
export default {
components: {
FormWizard,
TabContent,
},
data() {
return {
loadingWizard: false,
error: null,
count: 0,
dash: '-',
firstTime: 0,
model: {},
html: '',
index: '',
activeTabIndex: 0,
tabs: [{title: 'Personal details', icon: 'ti-user', component: 'firstTabSchema'},
{title: 'Is Logged In?', icon: 'ti-settings', component: 'secondTabSchema', hide: false},
],
formOptions: {
validationErrorClass: "has-error",
validationSuccessClass: "has-success",
validateAfterLoad: true,
validateAfterChanged: true,
},
}
},
created() {
this.html = this.tree;
this.index = this.ind;
},
props: ['tree', 'ind'],
methods: {
onComplete: function () {
alert('Yay. Done!');
},
setLoading(value) {
this.loadingWizard = value
},
handleChange(prevIndex, nextIndex) {
console.log(`Changing from ${prevIndex} to ${nextIndex}`)
},
setError(error) {
this.error = error
},
validateFunction: function () {
return new Promise((resolve, reject) => {
console.log(this.$refs.firstTabSchema);
setTimeout(() => {
if (this.count % 2 === 0) {
reject('Some custom error')
} else {
resolve(true)
}
this.count++
}, 100)
})
},
validate() {
return true
},
buildTree(tree, rep = 1) {
var html = '';
var app = this;
var dash = "--";
app.html = tree;
var test = this.formOptions;
for (var key in app.html) {
var isComponentExists = key in Vue.options.components
if(!isComponentExists) {
Vue.component(key, {
template: `<div :class="key"><vue-form-generator :model="model"
:schema="schema"
:options="formOptions"
ref="key"></vue-form-generator>{{ key }}</div>`,
mounted() {
this.schema = app.html[key]
this.key = key
},
data: function () {
return {
schema: app.html[key],
key: '',
formOptions: this.formOptions,
model: this.model,
}
},
}
)
//console.log(Vue.$options);
this.$emit('init')
}
}
}
},
watch: {
tree: {
handler() {
this.html = '';
this.buildTree(this.tree)
},
deep: true
}
}
}
</script>
So if I understand you correctly you are trying to use a list of some kind app.html to dynamically register a set of identical components under different names (key). I think it is possible, but i cannot tell from the code you provided what is going wrong.
I can tell you that this approach to code reuse/abstraction is probably not the right way to go. The whole point of components is that you can reuse functionality with the use of binding props. What you are trying to do is probably better achieved like this:
Vue.component('my-custom-form', {
props: ['key', 'schema', 'formOptions', 'model'],
template: `
<div>
<vue-form-generator
:model="model"
:schema="schema"
:options="formOptions"
:ref="key"
></vue-form-generator>{{ key }}
</div>`,
})
Then in your vue template:
<my-custom-form
v-for="(key, value) in app.html"
:key="key"
:schema="value"
:formOptions="formOptions"
:model="model"
/>
Let me know if that helps. Otherwise, if you are sure you want to stick with your original approach give me some more context for the code and I will see what i can do. Best of luck!
I think i understand a little bit better where you are getting stuck. I see this piece of code in your jsfiddle:
<div id="app">
<div>
<form-wizard #on-complete="onComplete">
<tab-content v-for="tab in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<component :is="tab.component"></component>
</tab-content>
</form-wizard>
</div>
</div>
You don't need to use the component :is syntax to solve this problem. You could also write is as follows:
<div id="app">
<div>
<form-wizard #on-complete="onComplete">
<tab-content v-for="(tab, tabindex) in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<my-custom-form v-if="tabindex == 1" :key="'the first key'" :schema="app.html['the first key']"/>
<my-custom-form v-else-if="tabindex == 2" :key="'the second key'" :schema="app.html['the second key']"/>
</tab-content>
</form-wizard>
</div>
</div>
etc. Let me know if that example is clear.
best

How to create a map using Here maps api and Vue.js

I want to load a simple map using Vue.js.
I created a component named map to load the basic map but somethings goes wrong and I tried many things but nothings works with me.
On my index.html I put all the javascript api from Here maps.
I am try to use this sample as a start point.
So,anyone has a Ideia what I am duing wrong?
Thanks.
https://developer.here.com/documentation/maps/topics/quick-start.html
<template>
<div>
<div id="mapContainer">
</div>
</div>
</template>
<script>
export default {
name: 'maps',
data: function() {
return {
map: null,
maptypes: null
}
},
mounted () {
this.initMap ();
},
methods: {
initMap() {
// Initialize the platform object:
var platform = new H.service.Platform({
app_id: 'nyKybJs4fZYfMCd7jfsx',
app_code: 'E_xE5837hGk33SL8M6hWIg',
useCIT: true,
useHTTPS: true
});
this.maptypes = platform.createDefaultLayers();
// Instantiate (and display) a map object:
this.map = new H.Map(
document.getElementById('mapContainer'),
this.maptypes.normal.map,
{
zoom: 10,
center: { lng: 13.4, lat: 52.51 }
});
}
}
}
</script>
<template>
<div>
<div style="width: 100%; height: 500px" id="map-container"></div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data: () => ({ map: null }),
mounted () {
// Initialize the platform object:
const platform = new H.service.Platform({
app_id: 'nyKybJs4fZYfMCd7jfsx',
app_code: 'E_xE5837hGk33SL8M6hWIg',
useCIT: true,
useHTTPS: true,
});
const maptypes = platform.createDefaultLayers();
// Instantiate (and display) a map object:
this.map = new H.Map(
this.$el.getElementById('map-container'),
maptypes.normal.map,
{
zoom: 10,
center: { lng: 13.4, lat: 52.51 },
},
);
},
}
</script>
You should provide explicit size of map container. For example:
<template>
<div>
<div style="width: 100%; height: 500px" id="mapContainer"></div>
</div>
</template>