Highmaps(HighCharts) loads same map when drilling down - vue.js

I'm faced with a problem while implementing Highmaps.
In my vue project with Webpack, I followed each step explained https://github.com/highcharts/highcharts-vue to use Highmaps.
Here's my main.js file
//highchart
import HighchartsVue from 'highcharts-vue'
import Highcharts from 'highcharts'
import WorldMap from '#highcharts/map-collection/custom/world.geo.json'
import USMap from '#highcharts/map-collection/countries/us/us-all.geo.json'
import mapInit from 'highcharts/modules/map.js'
import DrillDown from 'highcharts/modules/drilldown.js'
mapInit(Highcharts);
DrillDown(Highcharts);
Highcharts.maps['world'] = WorldMap;
Highcharts.maps['us'] = USMap;
And, here's my vue file
<template>
<div>
<mymap :mapOptions="mapOptions.options"></mymap>
</div>
</template>
<script>
export default{
data(){
return {
mapOptions: {
options: {
chart: {
map: 'us',
events: {
drilldown: function(e){
var chart = this;
this.addSeriesAsDrilldown(e.point, {
data: 'world',
dataLabels: {
enabled: true,
format: '{point.name}'
}
})
},
drillup: function(){
}
}
},
series: [{
data: data,
joinBy: ['postal-code', 'code'],
dataLabels: {
enabled:true,
color: '#FFFFFF',
format: '{point.name}'
}
}],
}
}
}
},
}
</script>
Because the chart has 'chart': 'us' option, it renders US map first. Then, when I click any state (just for a test) it switches US map to world map.
It renders world map. However, it also render US map too. That is, it renders both two maps. How can I fix it??

Related

How to use lottie with NuxtJS to have hover, click and all functions working?

I'm using the 'vue-lottie' package and there's not much information about how to use it.
I got the JSON animations from Lordicons and it shows correctly but I can't make the animation work on hover or click, only loop or static (no animation).
My Component:
<template>
<div>
<lottie
:options="lottieOptions"
:width="50"
/>
</div>
</template>
<script>
import lottie from "vue-lottie/src/lottie.vue";
import * as animationData from "~/assets/about.json";
export default {
components: {
lottie
},
data() {
return {
anim: null, // for saving the reference to the animation
lottieOptions: {
animationData: animationData.default,
loop: false,
autoplay: false,
}
};
},
methods: {
handleAnimation(anim) {
this.anim = anim;
},
stop() {
this.anim.stop();
},
play() {
this.anim.play();
},
pause() {
this.anim.pause();
},
}
};
</script>
And I'm using on the page importing the component only:
...
<AboutIcon />
...
<script>
import AboutIcon from "~/components/AboutIcon.vue";
export default {
components: {
AboutIcon,
},
data() {
return {};
}
};
</script>
From the code sample you provided, you forgot to attach the #animCreated="handleAnimation" event.
So this.anim is actually always null.
<template>
<div>
<lottie
:options="lottieOptions"
:width="50"
#animCreated="handleAnimation"
/>
</div>
</template>
Then you just have to set a #mouseover="play" to start the animation on hover.

Create dynamic Google Map Markers with SVGs

I am trying to create Google Map Markers with dynamic information in it. For that feature I am using a Vuejs-Component with a SVG as template and add a "text"-Tag.
Example:
<template>
<svg width="40px" height="56px" viewBox="0 0 40 56" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<text>{{ text }}</text>
</svg>
</template>
<script>
export default {
name: "DynamicGMapMarker",
props: {
text: {
type: String,
required: true
}
}
}
</script>
Then I am creating the SVG via a helper function and create a base64 data url:
import Vue from 'vue'
import DynamicGMapMarker from './DynamicGMapMarker'
const DynamicGMapMarkerConstructor = Vue.extend(DynamicGMapMarker)
export const getDynamicMarkerIcon = (text) => {
const iconComponent = new DynamicGMapMarkerConstructor({
propsData: {
text
}
});
iconComponent.$mount();
const iconDom = iconComponent.$el;
const iconString = new XMLSerializer().serializeToString(iconDom);
return 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(iconString);
}
Now, I can use the getDynamicMarkerIcon() function. This is working in vue.js, but not in nuxt.js. In nuxt.js we need to add a XML-Serializer from npm, since nuxt can not use the Browser's XMLSerializer. For that I am using teclone/xml-serializer.
Now, the Problem seems to be that Nuxt can not create a DOM, because of SSR. When I am trying to use this method:
<template>
<div id="app">
<GmapMap
:center="center"
:zoom="15"
:options="options"
id="map"
>
<GmapCluster :zoomOnClick="true">
<GmapMarker
:key="index"
v-for="(marker, index) in markers"
:position="marker.coordinates"
:clickable="true"
:draggable="false"
:title="marker.name"
:icon="getMarker(marker)"
/>
</GmapCluster>
</GmapMap>
</div>
</template>
<script>
import GmapCluster from 'vue2-google-maps/dist/components/cluster'
import {getMarkerIcon} from "./MapIconUtil"
export default {
name: 'App',
components: {
GmapCluster
},
methods: {
getMarker(marker) {
return getDynamicMarkerIcon(marker.text);
}
},
data() {
return {
options: {
zoomControl: false,
mapTypeControl: false,
scaleControl: false,
streetViewControl: false,
rotateControl: false,
fullscreenControl: false,
disableDefaultUi: true,
clickableIcons: false
},
center: {
lat: 11,
lng: 11
},
"markers": [
{
coordinates: {
name: "Test",
lat: 11.0001,
lng: 11.0001,
text: "101"
}
}
]
}
}
}
</script>
I get an error that iconComponent.$el is undefined. I can not mount the SVG inside a DOM.
Are there any possibilities to tell Nuxt, that this component should run on browser side only?
I have already tried to wrap GmapMarker in <client-only>-Tag. That did not work. Can I use something like a "virtual DOM"? Any other ideas how to make it work?
I did it. You can selectively change the "mode" in which nuxt will render a specific url. Here you can see what configurations are possible.
You have to create a server middleware and tell nuxt if a path should be rendered in spa. Otherwise just default to your mode specified in your nuxt.config.js.
Check this out for an example.

Using custom model objects as provider for v-for in Vue?

new to Vue - learning as I go about exploring.
I'm trying to use a custom object as data source for a v-for situation. So far, not working - I admit I'm not sure how to access data by name in a Vue component.
main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import VueKonva from "vue-konva";
Vue.use(VueKonva);
import RoomBuilder from "#/model/RoomBuilder";
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App),
data: () => ({
roomBuilder: new RoomBuilder()
})
}).$mount("#app");
The custom object, which may not be wired right into the data provider, is roomBuilder.
Then, in the component where I want to reference this object, I have this code:
<template>
<v-stage ref="stage" :config="stageSize">
<v-layer>
<v-rect ref="background" :config="backgroundConfig"></v-rect>
<v-rect
v-for="room in roomBuilder.getRooms()"
:key="room.id"
:config="room.config"
></v-rect>
</v-layer>
</v-stage>
</template>
<script>
export default {
name: "RoomGrid",
data() {
return {
stageSize: {
width: 1000,
height: 1000
},
backgroundConfig: {
width: 50,
height: 50,
fill: "#2B2B2B"
}
};
},
mounted() {
this.$nextTick(function() {
window.addEventListener("resize", this.fitStageIntoParentContainer);
this.fitStageIntoParentContainer();
});
},
methods: {
fitStageIntoParentContainer() {
const parentElement = this.$parent.$el;
const background = this.$refs.background.getNode();
background
.setAttr("width", parentElement.clientWidth)
.setAttr("height", parentElement.clientHeight);
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss"></style>
The two POJOs are:
RoomBuilder.js
import Room from "#/model/Room";
class RoomBuilder {
construct() {
this.rooms = [new Room(1)];
}
drawOnto(konvaStage) {
console.log(konvaStage);
}
getRooms() {
return this.rooms;
}
}
export default RoomBuilder;
and
Room.js
class Room {
construct(id) {
this.id = id;
this.config = {
x: 10,
y: 10,
fill: "white",
width: 10,
height: 10
};
}
}
export default Room;
Thanks, and go easy on me. I'm typically a server-side guy! I clearly have wired something wrong... have tried
v-for="room in this.$data.roomBuilder.getRooms()"
v-for="room in roomBuilder.getRooms()"
etc.
Clearly there's some magic afoot I don't understand!
Thank you!

Error in retrieving values to echart bar chart in vue

i am trying to use my firestore database data in vue chart but it is showing error with not defined
<vx-card title="PLACEMENT ANALYSIS" class="mb-base" >
<div class="mt-5">
<e-charts :options="bar" ref="bar" theme="ovilia-green" auto-resize />
</div>
</vx-card>
</template>
<script>
import ECharts from 'vue-echarts/components/ECharts'
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/legend'
import 'echarts/lib/chart/bar'
import theme from './theme.json'
import firebase from 'firebase/app'
import 'firebase/auth'
import "firebase/firestore"
ECharts.registerTheme('ovilia-green', theme)
export default {
data() {
return {
arr:[],
l:50,
r:30,
bar: {
legend: {},
tooltip: {},
dataset: {
// Provide data.
source: [
['Product', 'Eligible Students', 'Placed Students', ],
['B.Tech', ],
['MBA', this.random2(),this.random3()],
['B.com', this.random2(),this.random3()],
['MSc.', this.random2(),this.random3()],
['Others', this.random2(),this.random3()]
]
},
// Declare X axis, which is a category axis, mapping
// to the first column by default.
xAxis: { type: 'category' },
// Declare Y axis, which is a value axis.
yAxis: {},
// Declare several series, each of them mapped to a
// column of the dataset by default.
series: [{ type: 'bar' }, { type: 'bar' }]
},
}
},
beforeCreate(){
let u = firebase.auth().currentUser
firebase.firestore().collection('Colleges').doc(u.uid).get().then(doc =>{
this.arr = doc.data()
console.log(this.arr)
})
},
methods: {
random2() {
return[ this.arr.eligible]
},
random3(){
return[this.arr.placed]
}
},
components: {
ECharts
},
computed:{
chart(){
console.log(this.arr)
return this.arr
}
}
}
</script>
here eligible and placed are fields in arr and is visible in beforeCreate(),
but the chart is showing eligible is undefined and chart is not visible.
i tried using seperate varibales e.g l and r in data return field but it still shows undefined .
I am importing echart on different page as
<echarts-bar-chart></echarts-bar-chart>
<script>
import EchartsBarChart from '../charts-and-maps/charts/echarts/EchartsBarChart.vue'
<echarts-bar-chart></echarts-bar-chart>
Components:{
EchartsBarChart
}
</script>
arr is an array and you are trying to use a property arr.eligible that does not exist on an array. Do you want an item property, such as arr[0].eligible?
You might also want to confirm the this in your async callback is actually your component. Sometime a closure is needed to capture it.
You need to make sure this refers to your component. Try to capture it in a closure. Usually the lambda works, but sometimes there are issues.
const that = this;
firebase.firestore().collection('Colleges').doc(u.uid).get().then(doc =>{
that.arr = doc.data()
console.log(that.arr)
console.log(that)
})

HighCharts Vue JS - CSV key

I'm using csv-loader from webpack to load my CSV file and it loads fine on my Vue JS app.
The data.csv key is populated but the chart is not displayed.
App is being served locally.
<template>
<div id="app">
<img src="./assets/logo.png">
<highcharts :options="chartOptions"></highcharts>
</div>
</template>
<script>
import {Chart} from 'highcharts-vue'
import csvPath from './assets/test.csv'
import Papa from 'papaparse'
export default {
name: 'app',
components: {
highcharts: Chart
},
data () {
return {
chartOptions: {
chart: {
type: 'column'
},
data: {
csv: csvPath
},
title: {
text: 'Fruit Consumption'
},
yAxis: {
title: {
text: 'Units'
}
}
}
}
},
CSV File
Categories,Apples,Pears,Oranges,Bananas
John,8,4,6,5
I expect to see a fully plotted graph (barchart).
Try to import and initialize the data Highcharts module like that:
import Highcharts from "highcharts";
import dataModule from "highcharts/modules/data";
dataModule(Highcharts);