VueJS - VueChart JS not getting updated on store.dispatch - vue.js

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.

Related

Assigning data to fields on opening dialog in vuetify

I've created a dialog box using vuetify and I want it to be prepopulated with data in the v-select but it's blank when I open the dialog modal. I've assigned the propPackage to the selectPackage which is used in v-model in the v-select. How should I prepopulate it when I open the dialog?
Dialog
<template>
<v-row justify="center">
<v-dialog v-model="dialog" max-width="600px" #click:outside="close">
<v-select
:items="['Basic', 'Standard', 'Premium']"
label="Package*"
required
v-model="selectPackage"
></v-select>
<v-btn #click="close"> Close </v-btn>
</v-dialog>
</v-row>
</template>
<script>
export default {
props: {
dialog: {
type: Boolean,
required: false,
default: false,
},
propPackage: {
type: String,
required: true,
},
},
data: () => ({
selectPackage: this.propPackage,
}),
methods: {
close() {
this.$emit("close");
},
},
};
</script>
Parent component
<template>
<v-btn #click="bookDialog('Basic')"></v-btn>
<form-modal :dialog="openDialog" #close="close" :propPackage="propPackage" />
</template>
<script>
import FormModal from "#/components/FormModal.vue";
export default {
components: {
FormModal,
},
data: () => ({
openDialog: false,
propPackage: null,
}),
methods: {
bookDialog(val) {
this.propPackage = val;
this.openDialog = true;
},
close() {
this.openDialog = false;
},
},
};
</script>
Check this codesandbox I made: https://codesandbox.io/s/stack-70077413-943o6?file=/src/components/FormModal.vue
The main issue is that you're trying to access the prop value directly on your data block:
data: () => ({
selectPackage: this.propPackage,
}),
In this case, it would be better to get the prop value by setting up a watcher instead, just like this:
data: () => ({
selectPackage: ''
}),
watch: {
propPackage(val) {
// Be sure to validate default values
if(val !== '') {
this.selectPackage = val
}
}
},
This way, you can also validate the prop value if you need to.
I added a few more comments in the codesanbox on things you could improve. Since the FormModal component works mainly as a dialog, you can use the 'value' prop and set up a computed property to be able to close the dialog directly from the child component, this way you avoid emitting a #close event to the parent component and the prop mutation warning.
Since you are using arrow functions for data section, this.propPackage will be undefined since this won't refer to vue instance. You can fix that in 2 ways:
Change the arrow function to ES6 notation in your dialog component:
data() {
selectPackage: this.propPackage,
},
Pass the vue instance as a parameter to arrow function and access your prop using that:
data: (instance) => ({
selectPackage: instance.propPackage,
}),
Once you populate your selectPackage data property the right way, your v-select will be populated with the value once you open your dialog.

How to open vuetify dialog after user logs in to application

In my application I want to show a modal to introduce the user in my application, so it will appear only in the first time he logs in. What I am doing is storing isNewUser in the global state and using it to know if it should render the modal or not using the same process described in this answer. (I'm not using event bus)
Here is my parent component:
<template>
<Intro :value="isNewUser" #input="finishTutorial" />
</template>
mounted() {
const store = this.$store;
this.isNewUser = store.state.auth.user.isNewUser;
},
When the user logs in and this component is rendered I saw the dialog being rendered and closing. If I hit f5 it reloads the page and dialog is showed correctly.
If I do the bellow modification it works, but I don't want to solve the problem this way since it won't work for all cases, it will depend on the speed of the user computer/internet.
mounted() {
setTimeout(() => {
const store = this.$store;
this.isNewUser = store.state.auth.user.isNewUser;
}, 2000);
},
I've tried using v-if as well
<template>
<Intro v-if="isNewUser" :value="true" #input="finishTutorial" />
</template>
<script>
export default {
components: {
Intro,
},
data() {
return {
isNewUser: false,
};
},
mounted() {
const store = this.$store;
this.isNewUser = store.state.auth.user.isNewUser;
},
methods: {
async finishTutorial() {
this.$store.dispatch('auth/finishTutorial');
this.isNewUser = false;
},
},
};
</script>
You can use a computed property to do so:
computed: {
isNewUser() {
return this.$store.state.auth.user.isNewUser;
}
}
and in the template you would do like so:
<template>
<Intro :value="isNewUser" #input="finishTutorial" />
</template>

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

VueJS2/Vuex Computed property not behaving as expected

I have a datastore with a submodule, both having one variable:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
namespaced: true,
state: {
foo: true,
},
modules: {
secondmodule: {
namespaced: true,
state: {
bar: 0,
},
mutations: {
updateBar: (state, value) => {
state.bar = value;
}
}
}
}
})
In my app, I want to display a snackbar for 2 seconds when the value of foo changes to true:
<template>
<v-app id="example-1">
<router-link :to="{ name: 'secondpage'}">Link</router-link>
{{data.connected}}
{{this.$dataStore.state.foo}}
<main>
<v-fade-transition mode="out-in">
<router-view></router-view>
</v-fade-transition>
</main>
<v-snackbar success top :timeout="2000" v-model="data.connected">
connection established!
</v-snackbar>
</v-app>
</template>
<script type="text/babel">
export default {
computed:{
data() {
return {
connected: this.$dataStore.state.foo,
bar: this.$dataStore.state.secondmodule.bar
}
}
},
};
</script>
Since foo is initialized with true, the snackbar is shown on startup-
Strangely, when I click the link to secondpage, which mutates the bar variable:
created() {
this.$dataStore.commit('secondmodule/updateBar', 1)
}
the snackbar shows up again and I observed that connected quickly changed to false and true again after clicking the link. The value foo is always true and has never changed, so how come the computed property connected changes?
Edit: Since I failed to provide a working fiddle, I added the code here, if anyone would like to take a look at it
https://github.com/netik25/vueProblem

Vue.js - Keep Alive Component - Error next Tick

Description
I'm trying to take advantage of the keep-alive functionality of vue-js 2.3 so my AJAX call is made only once.
Problem
The second time I try to open the popup component I get this error :
Error in nextTick: "TypeError: Cannot read property 'insert' of undefined"
TypeError: Cannot read property 'insert' of undefined
Steps
Click on the button to display the popup
Wait for one second
Close the popup
Click again on the button
https://jsfiddle.net/4fwphqhv/
Minimal reproduction example
<div id="app">
<button #click="showDialog = true">Show Component PopUp</button>
<keep-alive>
<popup v-if="showDialog" :show-dialog.sync="showDialog"></popup>
</keep-alive>
</div>
<template id="popup">
<el-dialog :visible.sync="show" #visible-change="updateShowDialog">{{asyncData}}</el-dialog>
</template>
Vue.component('popup', {
template: '#popup',
props : ['showDialog'],
data(){
return {
show: this.showDialog,
asyncData: "Loading please wait"
}
},
methods: {
updateShowDialog(isVisible) {
if (isVisible) return false;
this.$emit('update:showDialog', false )
}
},
created:function (){
const _this = this
setTimeout(() => _this.asyncData = 'Async Data was loaded' , 1000)
},
});
var vm = new Vue({
el: '#app',
data: {
showDialog: false,
},
});
Real code of the popup component
<template>
<el-dialog title="Order in progress" size="large" :visible.sync="show" #visible-change="updateShowLoadOrder"></el-dialog>
</template>
<script>
let popUpData;
export default {
name: '',
data () {
return {
ordersInProgress: [],
show: this.showLoadOrder
}
},
props: ['showLoadOrder'],
methods: {
updateShowLoadOrder (isVisible) {
if (isVisible) return false;
this.$emit('update:showLoadOrder', false)
}
},
created () {
const _this = this;
if (!popUpData) {
axios.get('api/mtm/apiGetOrdersInProgress').then((response) => {
_this.ordersInProgress = popUpData = response.data;
});
} else {
this.ordersInProgress = popUpData;
}
}
}
</script>
Ok. So your problem here is the wrong life-cycle hook.
If you change created to activated... it should work. It did for me in your JS fiddle.
activated:function (){
setTimeout(() => this.asyncData = 'Async Data was loaded' , 1000)
}
There are two other hooks, activated and deactivated. These are for keep-alive components, a topic that is outside the scope of this article. Suffice it to say that they allow you to detect when a component that is wrapped in a tag is toggled on or off. You might use them to fetch data for your component or handle state changes, effectively behaving as created and beforeDestroy without the need to do a full component rebuild.
SOURCE: here