I'm using vue-chartjs with Laravel, and I have a problem where the data isn't loaded when on load. But when I click 3x on Legend the data shows up.
Here is gif: https://gfycat.com/gifs/detail/SimilarShockedAffenpinscher
My code:
<template>
<Chart :chartData="this.data" :width="800" :height="500"></Chart>
</template>
<script>
import Chart from './Chart.vue';
export default {
name: 'home',
components: {
Chart
},
data() {
return {
data: {},
weights: [],
dates: []
}
},
mounted () {
this.render()
},
methods: {
render() {
self = this
axios.get('/api/data')
.then(function (response) {
for (var i = 0; i < response.data.data.length; i++) {
self.weights.push(response.data.data[i].kg)
self.dates.push(response.data.data[i].created_at)
}
console.log(response)
})
.catch(function (error) {
console.log(error);
});
// Overwriting base render method with actual data.
self.data = {
labels: self.dates,
datasets: [
{
label: 'Data One',
data: self.weights,
}
]
}
}
}
}
</script>
Chart.vue:
<script>
import VueCharts from 'vue-chartjs'
import { Bar, Line, mixins } from 'vue-chartjs'
import moment from 'moment'
import axios from 'axios'
export default {
extends: Bar,
mixins: [mixins.reactiveProp],
props: ['chartData', 'options'],
data() {
return {
}
},
mounted () {
this.renderChart(this.chartData, this.options)
},
methods: {
}
}
</script>
I suspect this is your problem:
self.data = {
labels: self.dates,
datasets: [
{
label: 'Data One',
data: self.weights,
}
]
}
Self is referencing this so self.data = this.data. it's assigning directly to your components data property.
Related
I use vue js and I display a graph with chartjs. When I click on the graph I want emit an event for get data in parent component. My onClick function works but not the event.
Do you have an idea of my problem ?
Component Line2.vue
<script>
import { mixins, Line } from "vue-chartjs";
const { reactiveProp } = mixins;
export default {
extends: Line,
mixins: [reactiveProp],
props: ["options"],
mounted() {
const self = this;
console.log(self);
this.options.onClick = function (e, tooltipItems) {
console.log(tooltipItems[0].__ob__); //.logged
self.$emit("sendIndex", tooltipItems[0]._index);
};
this.renderChart(this.chartData, this.options);
},
};
</script>
<style scoped></style>
My main component
...
<h1 v-on:sendIndex="getIndexCoord($event)">{{ indexCoord }}</h1>
...
methods: {
getIndexCoord: function (e) {
console.log("rrr", e); //Not logged
this.indexCoord = e;
},
}
Regards
1.first you create EventBus.js file
import Vue from 'vue';
export const EventBus = new Vue();
2.In your char.js file code like below
import { EventBus } from "../EventBus.js";
import { mixins, Line } from "vue-chartjs";
const { reactiveProp } = mixins;
export default {
extends: Line,
mixins: [reactiveProp],
props: ["options"],
mounted() {
const self = this;
console.log(self);
this.options.onClick = function (e, tooltipItems) {
console.log(tooltipItems[0].__ob__); //.logged
EventBus.$emit("sendIndex", tooltipItems[0]._index);
};
this.renderChart(this.chartData, this.options);
},
};
where you want to access your data in that file like below
import { EventBus } from "../EventBus.js";
mounted() {
EventBus.$on('sendIndex', data => {
console.log(data)
});
},
I'm using chartJs to add a Doughnut chart on my app, but I need to setup it's color under <script> and I'd like to get the color from theme/variables.css .
In the code below, I have a hardcoded backgroundColor, but I'd like to get it from --ion-color-secondary and --ion-color-secondary-contrast instead. How can I achieve that?
Here is the code:
<template>
<Doughnut
:chart-data="chartData"
:chart-options="chartOptions"
/>
</template>
<script lang=ts>
import { defineComponent, PropType } from 'vue'
import { Doughnut } from 'vue-chartjs'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
ArcElement,
CategoryScale,
Plugin
} from 'chart.js'
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale)
export default defineComponent<{fastdata: any}>({
name: 'MrProgress',
components: {
Doughnut
},
inject: ["fastdata"],
setup() {
const chartOptions = {
responsive: true,
maintainAspectRatio: true,
cutout: "90%",
borderWidth: 0,
}
return {
chartOptions,
};
},
computed: {
chartData() {
const pts = this.fastdata.user.pts;
const missing = this.fastdata.user.proximo_nivel - pts;
return {
datasets: [
{
backgroundColor: ['#41B883', '#e4e0d8'],
data: [pts, missing]
}
]
}
}
}
})
</script>
Following the tip from #EstusFlask in the comment regarding userCssVar, I manged to solve it in the following way:
npm i #vueuse/core
...
import { useCssVar } from '#vueuse/core'
import { ref } from 'vue'
<script>
...
const color_pts = useCssVar(ref('--ion-color-secondary'), ref(null)).value;
const color_missing = useCssVar(ref('--ion-background-color'), ref(null)).value;
return {
datasets: [
{
backgroundColor: [color_pts, color_missing],
data: [pts, missing]
}
]
}
...
</script>
Does anyone know why my state.pages doesnt get filled with the json data I fetch with axios? However, when I change something in my file and save it, so that vite will reload, the data does show up on the page. And will dissapear again when I refresh the page in the browser.
PageView:
<template>
<main v-if="page">
<h1>{{ page.title }}</h1>
<div class="content" v-html="page.content"></div>
</main>
<main v-else>
<h1>loading</h1>
</main>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
data() {
return {
page: null,
};
},
methods: {
...mapActions({ getPages: "getPages" }),
},
computed: {
slug() {
return this.$route.params.slug;
},
getPage() {
console.log(this.$store.state.pages);
return (this.page = this.$store.state.pages.find(
(page) => page.slug === this.slug
));
},
},
mounted() {
this.$store.dispatch("getPages");
this.getPages();
this.getPage;
},
};
</script>
<style>
</style>
Vuex index:
import { createStore } from 'vuex';
import axios from 'axios';
const store = createStore({
state() {
return {
pages: [],
};
},
mutations: {
setPages(state, { response }) {
state.pages = response.data;
},
},
actions: {
getPages({ commit }) {
axios.get('../src/data/pages.json').then((response) => {
commit('setPages', { response });
});
},
},
getters: {},
});
export default store;
The race condition results because promises weren't correctly chained. As a rule of thumb, every function that uses promises should chain all promises in use and return a promise as a result of its work.
getPages is dispatched twice.
It should be:
getPages({ commit }) {
return axios....
And:
mounted() {
return this.getPages()
.then(() => {
...
});
},
I would like to test if my getter is called in my component or view, but I don't understand how can it.
My method in the component :
computed: {
...mapGetters({ item: 'moduleA/getData' }),
},
And this is my unit test declaration :
beforeEach(() => {
store = new Vuex.Store({
modules: {
moduleA: {
state: {},
getters: {
getData() {
return { item: { name: 'test' } };
},
},
},
},
});
wrapper = shallowMount(MyComponent, {
store,
});
});
And I try to test if the data is loaded in my template:
it('should check if name is thruthy', () => {
expect(wrapper.find('.classA').text()).toEqual('test');
});
my component :
<template>
<v-content>
<ComponentA v-if="item.name"
key="keyA" ></ComponentA>
<ComponentB v-else key="keyA"></ComponentB>
</v-content>
</template>
<script>
import { mapGetters } from 'vuex';
import ComponentA from '#/components/ComponentA.vue';
import ComponentB from '#/components/ComponentB.vue';
export default {
/**
* Component's name :
*/
name: 'ViewA',
/**
* Component's used :
*/
components: {
ComponentA,
ComponentB,
},
/**
* computed's used :
*/
computed: {
...mapGetters({ item: 'moduleA/getData' }),
},
};
</script>
and my getter in the moduleA :
const getters = {
getData: state => state.data,
};
But I have this message:
TypeError: Cannot read property 'name' of undefined
Why? Thank you for your help.
As shown in docs, shallowMount's options has field named localVue. To solve your problem you should create a localVue, using createLocalVue and pass it to shallowMount.
createLocalVue returns a Vue class for you to add components, mixins
and install plugins without polluting the global Vue class.
import Vuex from 'vuex'
import { createLocalVue, shallowMount } from '#vue/test-utils'
beforeEach(() => {
const localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
modules: {
moduleA: {
state: {},
getters: {
getData() {
return { item: { name: 'test' } }
},
},
},
},
})
wrapper = shallowMount(MyComponent, {
localVue,
store,
})
})
I want to pass a value from my vue instance to my vue component.
I have following vue component (vuejs dropzonejs component), which das basically a nice Image upload ui.
<template>
<vue-dropzone ref="myVueDropzone" id="dropzone" :options="dropzoneOptions"></vue-dropzone>
</template>
<script>
import vue2Dropzone from 'vue2-dropzone'
import 'vue2-dropzone/dist/vue2Dropzone.css'
export default {
name: 'app',
components: {
vueDropzone: vue2Dropzone
},
data: function() {
return {
game: {
title: ''
},
dropzoneOptions: {
url: '/games/upload',
method: 'post',
params: {
title: this.game.title
}
}
}
},
methods: {
processFiles () {
this.$refs.myVueDropzone.processQueue();
},
getGameTitle () {
this.game.title = '',
}
}
}
</script>
I Need to get the game title from my vue instance and pass it to my vue component so that I can do some work there with that title.
My vue instance Looks like this:
require('./bootstrap');
window.Vue = require('vue');
Vue.component('vue-dropzone', require('./components/ImageDropzoneComponent.vue'));
const app = new Vue({
el: '#app',
data: {
loading: false,
title: '',
},
methods: {
createGame (e) {
this.uploadImage();
axios.post('/games', {
title: this.title,
})
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.log(error);
})
},
uploadImage () {
this.$refs.myVueDropzone.processFiles()
},
getGameTitle () {
return this.title;
}
}
});
The tag:
<vue-dropzone ref="myVueDropzone"></vue-dropzone>