Error in retrieving values to echart bar chart in vue - vue.js

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

Related

Update element on button click is not working

I'm new using Vuejs so I have a simple page with an input and button like
<input type="text" value="10" v-model="model.rowsPerPage">
<button #click="setNewValue()"> </button>
<script lang="ts">
import { useContact } from '#/composition/useContact'
import { ContactModel } from '#/services/modules/contact/contact'
import { defineComponent, reactive } from '#vue/composition-api'
export default defineComponent({
name: 'Contact',
setup() {
const model = reactive<ContactModel>({
pageNumber: 1,
rowsPerPage: 10,
searchBy: null,
staringDate: null,
endingDate: null,
})
const {
data: contactList,
totalItems: totalItems,
isLoading: isLoading,
error: error,
} = useContact(model)
return {
contactList,
model,
isLoading,
totalItems,
error,
}
},
methods: {
setNewValue() {
model.rowsPerPage = 20
},
},
})
</script>
So what I want to do, is update the input value by 10 if the button is clicked, so as you can see I add a method at the end of the script
methods: {
setNewValue() {
model.rowsPerPage = 20
},
},
But it throws me an error:
Cannot find name model
Why I can't access to const model declared at the top of script? How can I solve this? Regards
inside the methods you need to reference the "this" object to have access to what's returned in data or setup
but if you're using vue 3 with the composition api, you can simply define the function inside setup and return it, your template will then have access to it
(also, I think you should keep your component html inside a template tag)

Highmaps(HighCharts) loads same map when drilling down

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??

Vue component computed not reacting

I have 2 components OperatorsList and OperatorButton.
The OperatorsList contains of course my buttons and I simply want, when I click one button, to update some data :
I emit select with the operator.id
This event is captured by OperatorList component, who calls setSelectedOperator in the store
First problem here, in Vue tools, I can see the store updated in real time on Vuex tab, but on the Components tab, the operator computed object is not updated until I click antoher node in the tree : I don't know if it's a display issue in Vue tools or a real data update issue.
However, when it's done, I have another computed property on Vue root element called selectedOperator that should return... the selected operator : its value stays always null, I can't figure out why.
Finally, on the button, I have a v-bind:class that should update when the operator.selected property is true : it never does, even though I can see the property set to true.
I just start using Vue, I'm pretty sure I do something wrong, but what ?
I got the same problems before I used Vuex, using props.
Here is my OperatorList code :
<template>
<div>
<div class="conthdr">Operator</div>
<div>
<operator-button v-for="operator in operators" :op="operator.id"
:key="operator.id" #select="selectOp"></operator-button>
</div>
</div>
</template>
<script>
import OperatorButton from './OperatorButton';
export default {
name: 'operators-list',
components : {
'operator-button': OperatorButton
},
computed : {
operators() { return this.$store.getters.operators },
selected() {
this.operators.forEach(op =>{
if (op.selected) return op;
});
return null;
},
},
methods : {
selectOp(arg) {
this.$store.commit('setSelectedOperator', arg);
}
},
}
</script>
OperatorButton code is
<template>
<span>
<button type="button" v-bind:class="{ sel: operator.selected }"
#click="$emit('select', {'id':operator.id})">
{{ operateur.name }}
</button>
</span>
</template>
<script>
export default {
name: 'operator-button',
props : ['op'],
computed : {
operator() {
return this.$store.getters.operateurById(this.op);
}
},
}
</script>
<style scoped>
.sel{
background-color : yellow;
}
</style>
and finally my app.js look like that :
window.Vue = require('vue');
import Vuex from 'vuex';
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
const store = new Vuex.Store({
state: {
periods : [],
},
mutations: {
setInitialData (state, payload) {
state.periods = payload;
},
setSelectedOperator(state, payload) {
this.getters.operateurs.forEach( op => {
op.selected = (op.id==payload.id)
})
},
},
getters : {
operators : (state) => {
if (Array.isArray(state.periods))
{
let ops = state.periods
.map( item => {
return item.operators
}).flat();
ops.forEach(op => {
// op.selected=false; //replaced after Radu Diță answer by next line :
if (ops.selected === undefined) op.selected=false;
})
return ops;
}
},
operatorById : (state, getters) => (id) => {
return getters.operators.find(operator => operator.id==id);
},
}
});
import Chrono from './components/Chrono.vue';
var app = new Vue({
el: '#app',
store,
components : { Chrono },
mounted () {
this.$store.commit('setInitialData',
JSON.parse(this.$el.attributes.initialdata.value));
},
computed: {
...mapState(['periods']),
...mapGetters(['operators', 'operatorById']),
selectedOperator(){
this.$store.getters.operators.forEach(op =>{
if (op.selected) return op;
});
return null;
}
},
});
Your getter in vuex for operators is always setting selected to false.
operators : (state) => {
if (Array.isArray(state.periods))
{
let ops = state.periods
.map( item => {
return item.operators
}).flat();
ops.forEach(op => {
op.selected=false;
})
return ops;
}
}
I'm guessing you do this for initialisation, but that's a bad place to put it, as you'll never get a selected operator from that getter. Just move it to the proper mutations. setInitialData seems like the right place.
Finally I found where my problems came from :
The $el.attributes.initialdata.value came from an API and the operator objects it contained didn't have a selected property, so I added it after data was set and it was not reactive.
I just added this property on server side before converting to JSON and sending to Vue, removed the code pointed by Radu Diță since it was now useless, and it works.

VueJS - VueChart JS not getting updated on store.dispatch

I have something like accepting parameters from a form and submitting. On submit, I dispatch an action and return the response and assign them to the parameters of the chart. But the change is not happening unless i press the submit button twice. But when i press the submit button, the label is getting updates as there is a v-model linked to the label select. But since there is no v-model for the bar-chart component, it is not getting updated.
<template>
<v-container fluid>
<v-card class="small" v-if="!empty">
<bar-chart :chart-data="datacollection"></bar-chart>
</v-card>
</v-container>
</template>
<script>
import BarChart from './BarChart.js'
import { mapGetters, mapActions } from "vuex";
export default {
name : "TestLegPerformance",
components: {
BarChart
},
data: () => ({
startdate: null,
enddate: null,
startmodal: false,
endmodal: false,
datacollection : {
labels: [],
datasets: [
{
label: '',
backgroundColor: '#C58917',
data: []
}
]
},
selected: [],
empty: true
}),
computed: {
...mapGetters({
planNames: "planNames",
details: "details" //parameter that i return from getters
})
},
mounted () {
this.getAllPlanNamesAction();
},
methods: {
...mapActions(["getAllPlanNamesAction","getDetails"]),
//assigning values to the chart parameters
changeData(){
this.empty = false;
let collectionClone = Object.assign({}, this.datacollection);
collectionClone.datasets[0].label = this.selected;
collectionClone.labels = this.details.months;
collectionClone.datasets[0].data = this.details.sub_count;
this.datacollection = collectionClone;
console.log('Collection Clone: ',collectionClone)
},
// form submit action
submitAction(){
this.empty = true;
console.log("Plan: ",this.selected);
console.log("Start Date: ",this.startdate);
console.log("End Date: ",this.enddate);
this.$store.dispatch('getDetails',
[this.selected,this.startdate,this.enddate])
this.changeData();
}
}
}
</script>
Chart.js and Vue Chart.js are not reactive by default.
See this in the Vue Chart.js docs
See this example
So after a lot of tries, I got it to work by redirecting the submit button to two different functions and adding a timeout between those two functions.

Vue : Cant display passed data in component passed information with event bus

I have a vue application and a function that pushes a router and pass data to another router component.
passData: function () {
EventBus.$emit('passName', this.tableRow[0]);
this.$router.push('/analytic-two');
}
Then the other component.
<template>
<p>
This is Data passed from chart component {{passedRow}}
</p>
</template>
<script>
import { EventBus } from '../event-bus.js';
export default {
data() {
return {
passedRow: [
{
"name": "",
"numSongs": "",
"Year": ""
}
],
name: '',
id: '?',
};
},
created: function () {
const self = this;
EventBus.$on('passName', function (value) {
self.passedRow = value;
console.log(self.passedRow);
});
},
}
</script>
I know the data is coming through and its being logged but i can't figure out how to display it in my template does anyone have any ideas.
I would recommend to not using "=" operator instead of cleaning the reactive array and then fill it with the new values
self.passedRow.splice('deleteCount');
self.passedRow.push(...value); //pushes all the values in the value-array
This way you will at least be able see the progress.
You can also force a DOM-Update after setting the new value, but this should be unlikely in this case. To do so call in the component this.$forceUpdate()