Related
I want to apply sorting on firstname, lastname and email but i did not undestand How We sort table in ascending and descnding order by clicking on column name in vuejs. I am tried lot of concepts on stackoverflow and other resources but they didn't seem to work . This is very silly question but I am stopped on this from many days.I'm new to Vue.js and any help would be appreciated. Image
<template>
<div class="h-full flex flex-col">
<ListFilter
ref="filter"
:class="{ 'mb-8': hasFilters || search }"
:filter="filter"
:has-filters="hasFilters"
:has-quick-filters="hasQuickFilters"
:query="query"
:search="search"
:search-placeholder="searchPlaceholder"
:filter-classes="filterClasses"
:initial-values="initialFilter"
:apply-filter="values => applyFilter(filterMapper(values))"
:apply-search="applySearch"
:clear-search="clearSearch"
>
<template #filters="props">
<slot
name="filters"
:reset="props.reset"
:filter="filter"
/>
</template>
<template #quickfilter>
<slot
name="quickfilter"
:apply-filter="applyFilter"
/>
</template>
<template #loader>
<loader
:loading="loading"
:backdrop="true"
/>
</template>
</ListFilter>
<!-- <div>
</div> -->
<vuetable
ref="vuetable"
:row-class="rowClass"
:api-mode="false"
:fields="columns"
:data-manager="dataManager"
:css="css.table"
:track-by="trackBy"
:sort-order="innerSortOrder"
:detail-row-component="detailRow"
:detail-row-options="detailOptions"
:no-data-template="noDataTemplate"
pagination-path="pagination"
#vuetable:row-clicked="handleRowClicked"
#vuetable:cell-clicked="props => $emit('cell-clicked', props)"
#vuetable:pagination-data="onPaginationData"
>
<template #empty-result>
<slot name="empty-result" />
</template>
<template #actions="props">
<div v-if="hasActions">
<ListActions :record="props.rowData">
<slot
name="actions"
:record="props.rowData"
/>
</ListActions>
</div>
</template>
<template #inline-actions="props">
<div v-if="hasInlineActions">
<InlineActions :record="props.rowData">
<slot
name="inline-actions"
:record="props.rowData"
/>
</InlineActions>
</div>
</template>
<template #detail-toggler="props">
<div v-if="hasDetailRow">
<DetailToggler
:row-data="props.rowData"
:row-index="props.rowIndex"
>
<template #default="detailProps">
<slot
name="detail-toggler"
:record="props.rowData"
:is-open="detailProps.isOpen"
:toggle-row="detailProps.toggleRow"
/>
</template>
</DetailToggler>
</div>
</template>
</vuetable>
<div
v-if="!infinityScroll"
class="pt-2"
>
<vuetable-pagination
ref="pagination"
:css="css.pagination"
#vuetable-pagination:change-page="onChangePage"
/>
</div>
</div>
</template>
<script>
import { Vuetable, VuetablePagination } from 'vue3-vuetable';
import AuthMixin from '#/components/auth/AuthMixin';
import ModalNavigation from '#/mixins/ModalNavigation';
// import Loader from '#/components/ui/Loader';
import { throttle } from 'lodash-es';
import ListFilter from '#/components/auth/list/ListFilter';
import NotifyMixin from '#/mixins/NotifyMixin';
const css = {
table: {
tableClass: 'table-auto w-full table',
tableBodyClass: '',
tableHeaderClass: 'px-4 py-2',
tableWrapper: 'overflow-x-auto flex-1',
loadingClass: 'loading',
ascendingIcon: 'blue chevron up icon',
descendingIcon: 'blue chevron down icon',
ascendingClass: 'sorted-asc',
descendingClass: 'sorted-desc',
sortableIcon: 'grey sort icon',
handleIcon: 'grey sidebar icon',
detailRowClass: 'bg-blue-100',
},
pagination: {
wrapperClass: 'flex justify-center py-4',
activeClass: 'active',
disabledClass: '',
pageClass: 'btn-paging',
linkClass: 'btn-paging',
paginationClass: '',
paginationInfoClass: 'pagination-info',
dropdownClass: '',
icons: {
first: '',
prev: '',
next: '',
last: '',
},
},
};
export default {
components: {
// Loader,
Vuetable,
VuetablePagination,
ListFilter,
},
mixins: [NotifyMixin, AuthMixin, ModalNavigation],
props: {
css: {
type: Object,
default() {
return css;
},
},
},
data() {
return {
// loading: false,
query: '',
// filter: this.filterMapper(this.initialFilter),
sort: undefined,
showFilters: false,
loading: false,
scrollContainer: null,
innerSortOrder: this.sortOrder || [],
data: [],
columns: [
{
name: 'first_name',
title: 'First Name',
class: 'w-1/4',
},
{
name: 'last_name',
title: 'Last Name',
class: 'w-1/4',
},
{
name: 'email',
title: 'Email',
class: 'w-1/4',
},
{
name: 'phone_number',
title: 'Phone Number',
class: 'w-1/4',
},
{
name: 'communities',
title: 'Communities',
class: 'w-1/4',
},
{
label: 'communities',
field: 'Communities',
class: 'w-1/4',
formatter(value) {
// console.log('ROW', value);
let communities = [];
value.community_has_leasing_agents.forEach(function (row) {
communities.push('<span class="tag">' + row.community_id + '</span>');
});
// console.log('communities=>', communities);
return communities.join('');
},
},
],
};
},
computed: {
search() {
return true;
},
searchPlaceholder() {
return 'Search by name, community, email';
},
hasFilters() {
// return !!this.$slots.filters;
return true;
},
hasQuickFilters() {
// return !!this.$slots['quickfilter'];
return true;
},
hasActions() {
// return !!this.$slots.actions;
return true;
},
hasInlineActions() {
// return !!this.$slots['inline-actions'];
return true;
},
hasDetailRow() {
// return this.detailRow !== "";
return true;
},
hasClickRowListener() {
// return this.$attrs['row-clicked'];
return true;
},
nonClickableRow() {
return (
this.onRowClick === 'none' &&
!this.hasClickRowListener &&
!(this.$route.params?.passFlowTo && this.$route.params?.passFlowTo !== this.$route.name)
);
},
detailOptions() {
return {
...this.detailRowOptions,
vuetable: this.$refs.vuetable,
};
},
},
async mounted() {
console.log('== Leasing Agents Index ==');
console.log(this.profile);
await this.getLeasingAgentsFromAPI();
},
created() {
if (!this.community) {
this.notifyError('please select a community to continue, then refresh the browser');
}
},
methods: {
async getLeasingAgentsFromAPI() {
console.log('LEASING::getLeasingAgentsFromAPI()');
this.loading = true;
return await this.$calendarDataProvider
.get('calendarGetLeasingAgents', {
customer_id: this.profile.customerId,
})
.then(res => {
console.log('LEASING::getLeasingAgentsFromAPI() result:');
console.log(res);
this.data = res;
return res;
// return this.getEvents(res);
})
.catch(err => console.log(err.message))
.finally(() => {
this.loading = false;
});
},
rowClass(dataItem) {
const classes = ['table-row', 'row'];
if (this.nonClickableRow) {
classes.push('table-row-nonclickable');
}
if (this.hasDetailRow && this.$refs.vuetable?.isVisibleDetailRow(dataItem[this.trackBy])) {
classes.push('table-row-detail-open');
}
return classes.join(' ');
},
toggleFilters() {
this.showFilters = !this.showFilters;
},
applySearch(value) {
this.query = value;
},
clearSearch() {
this.query = '';
},
applyFilter(values) {
this.filter = values;
this.$refs.vuetable?.changePage(1);
this.$refs.vuetable?.resetData();
},
doSearch: throttle(
function () {
if (this.query.length > 2 || this.query.length === 0) {
this.reload();
}
},
500,
{ trailing: true }
),
onPaginationData(paginationData) {
if (!this.infinityScroll) {
this.$refs.pagination.setPaginationData(paginationData);
}
},
onChangePage(page) {
this.$refs.vuetable.changePage(page);
},
reload() {
this.$refs.vuetable.resetData();
this.$refs.vuetable.refresh();
},
getSort({ sortField, direction }) {
// sortField: 'type&bundle.name', - sort by two fields
const fields = sortField.split('&');
if (fields.length > 1) {
return fields.map(item => {
const [fieldName, dir = direction] = item.split('|');
return `${fieldName},${dir}`;
});
}
return `${sortField},${direction}`;
},
async dataManager(/*sortOrder = [], pagination = { current_page: 1 }*/) {
// allow static data
// if (this.data) {
// return {
// data: this.data,
// };
// }
const data = await this.getLeasingAgentsFromAPI();
return {
data: data,
};
// const prevData = this.$refs.vuetable?.tableData ?? [];
// const { pageSize: size, query, filter, sort: oldSort } = this;
// const sort = sortOrder[0] ? this.getSort(sortOrder[0]) : undefined;
// this.loading = true;
// return this[this.dataProvider]
// .getList(this.resource, {
// page: pagination.current_page - 1,
// size,
// sort,
// query,
// ...this.requestParams,
// ...filter,
// })
// .then(this.responseMapper)
// .then(({ content: nextData, totalElements }) => {
// const newPagination = this.$refs.vuetable?.makePagination(totalElements, this.pageSize, pagination.current_page);
// this.sort = sort;
// this.innerSortOrder = sortOrder;
// return {
// pagination: newPagination,
// data: this.infinityScroll && oldSort === sort ? [...prevData, ...nextData] : nextData,
// };
// })
// .catch(error => {
// this.notifyError(error.message);
// return {
// pagination: this.$refs.vuetable?.tablePagination,
// data: this.$refs.vuetable?.tableData,
// };
// })
// .finally(() => (this.loading = false));
},
handleRowClicked({ data, ...rest }) {
if (this.$route.params?.passFlowTo && this.$route.params?.passFlowTo !== this.$route.name) {
this.$router.push({
name: this.$route.params?.passFlowTo,
params: {
...this.$route.params,
[this.routeIdParam]: data[this.trackBy],
},
});
} else {
switch (this.onRowClick) {
case 'edit':
this.$router.replace({ path: `${this.basePath}/${data[this.trackBy]}` });
break;
case 'details':
this.$router.replace({ path: `${this.basePath}/${data[this.trackBy]}/details` });
break;
default:
this.$emit('row-clicked', { data, ...rest });
}
}
},
handleScrollContainer(e) {
if (this.$refs.vuetable.tablePagination && e.target.scrollHeight - e.target.scrollTop - e.target.clientHeight <= 2) {
this.onChangePage('next');
}
},
},
};
</script>
<style scoped>
.tag {
display: inline-flex;
align-items: center;
overflow: hidden;
margin-right: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
border-radius: 0.0625rem;
--bg-opacity: 1;
background-color: #eaf6ff;
background-color: rgba(234, 246, 255, var(--bg-opacity));
--text-opacity: 1;
color: #105d91;
color: rgba(16, 93, 145, var(--text-opacity));
font-family: 'FrankNew', sans-serif;
font-weight: 500;
}
</style>
I'm attaching my index.vue file and image.
How to remove line over the stroke line in area chart - Apexchart. the image
Please help me to fix this!
Here is my code so far
<template>
<div id="chart">
<apexchart
ref="pricesRef"
type="area"
height="150"
:options="options"
:series="series"
></apexchart>
<!-- <button #click="updateSeries">click</button> -->
</div>
</template>
<script>
import { axios } from 'boot/axios';
// import { dates, prices } from 'src/services/area-chart';
import { date } from 'quasar';
function formatNumber2(number, tofix) {
const val = (number / 1).toFixed(tofix).replace(',', ' ');
return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
let sharePrice = [];
const dataSharePrice = [];
export default {
name: 'AreaChart',
data() {
return {
timeInterval: 0,
dataSharePrice: [],
series: [
{
name: 'Share Price',
data: [
// { x: dates[0], y: prices[1] },
// { x: 1602392287529, y: 0.05 },
],
date: [],
},
],
options: {
chart: {
type: 'area',
zoom: {
enabled: false,
},
toolbar: {
show: false,
},
sparkline: {
enabled: true,
},
},
tooltip: {
custom({ series, w, dataPointIndex }) {
const unixTime = w.config.series[0].data[dataPointIndex].x;
const timeStamp = new Date(unixTime * 1000);
const hour = date.formatDate(timeStamp, 'DD MMMM');
return (
`
<div class="arrow_box_tooltip q-pa-md">
<span class="text-h6">
Date: ${hour}</span>
<br />
<span class="text-h6">
Share Price: $${formatNumber2(
series[0][dataPointIndex],
0,
)} USD</span>
</div>
`
);
},
},
noData: {
text: 'Loading...',
},
dataLabels: {
enabled: false,
},
stroke: {
curve: 'smooth',
lineCap: 'butt',
colors: undefined,
width: 2,
},
title: {
text: '',
align: 'left',
},
grid: {
show: false,
},
xaxis: {
type: 'datetime',
},
yaxis: {
opposite: true,
},
legend: {
horizontalAlign: 'left',
},
},
};
},
computed: {},
methods: {
getData() {
axios
.get(
'apiurl',
)
.then((response) => {
// console.log(response.data.prices);
sharePrice = response.data.prices;
})
.catch((err) => {
console.log(err);
});
},
updateSeriesData() {
for (const price of sharePrice) {
const unixTime = price[0];
if (Object.keys(price).length > 0) {
dataSharePrice.push({
x: unixTime,
y: price[1],
});
} else {
dataSharePrice.push({});
console.log('err');
}
// console.log(price[0], price[1]);
}
// console.log(dataSharePrice);
this.series[0].data = dataSharePrice;
this.$refs.pricesRef.updateSeries([
{
data: dataSharePrice,
},
]);
// console.log(this.$refs);
},
// timer() {},
},
mounted() {},
async created() {
await this.getData();
setTimeout(() => {
this.updateSeriesData();
// console.log('done');
}, 3000);
/* ; */
// this.timer();
},
beforeDestroy() {
// clearInterval(this.timer);
},
};
</script>
<style scoped>
/* #chart {
background-color: #18212f;
opacity: 1;
background-size: 7px 7px;
background-image: repeating-linear-gradient(
45deg,
#111726 0,
#111726 0.7000000000000001px,
#18212f 0,
#18212f 50%
);
} */
</style>
You can specify each stroke width separately by passing array
stroke: {
curve: 'smooth',
lineCap: 'butt',
colors: undefined,
width: [2,0],
},
I'm using ag-grid-vue and i'm trying to create custom tool tip.
This is my code:
CustomToolTipVue.js component:
export default {
template: `
<div class="custom-tooltip" v-bind:style="{ backgroundColor: color }">
<p><span>{{ data.athlete }}</span></p>
<p><span>Country: </span>{{ data.country }}</p>
<p><span>Total: </span>{{ data.total }}</p>
</div>
`,
data: function () {
return {
color: null,
athlete: null,
country: null,
total: null,
};
},
beforeMount() {
this.data = this.params.api.getDisplayedRowAtIndex(
this.params.rowIndex
).data;
this.color = this.params.color || 'white';
},
};
Grid component - where i'm using it (Attached only the main code):
<script>
import { AgGridVue } from "ag-grid-vue";
import "ag-grid-community";
import CustomToolTipVue from './CustomToolTipVue.js'
export default {
name: "Grid",
data: () => ({
gridOptions: null,
gridApi: null,
gridColumnApi: null,
selectedRows: [],
noRowsTemplate: "",
loadingTemplate: "",
rowClassRules:null
}),
components: {
AgGridVue,
CustomToolTipVue
},
created() {
this.gridOptions = {};
this.gridOptions.columnDefs = this.columnDefs;
this.gridOptions.components = {
};
},
computed: {
defaultColDef() {
return {
resizable: true,
menuTabs: ["filterMenuTab", "generalMenuTab", "columnsMenuTab"],
sortable: true,
filter: true,
tooltipComponentParams: { color: '#ececec' },
tooltipComponent: "CustomToolTipVue",
};
},
},
And i keep getting this error:
"Could not find component CustomToolTipVue, did you forget to configure this component?"
Any idea why?
Managed to solve it by upgrde ag-grid to latest version - 27.0.0
Hello i am trying to display different charts using the chartjs by calling the API. Below code shows how i have formatted the chart.vue
Chart.vue:
<template>
<div class="chart-container" style="position: relative; height: 40vh; width:100%;">
<slot name="test1"></slot>
<slot name="test2"></slot>
</div>
</template>
<script>
export default {
name: 'charts',
data () {
return {
date: [],
challenge: [],
data: []
}
},
mounted () {
this.check(8, 'chart_8')
this.check(7, 'chart_7')
},
methods: {
check (id, name) {
this.$http.get(`/api_chart/${ id }/full`)
.then((response) => {
this.date = response.data.date
this.challenge = response.data.challenge
this.data = this.date.map((date, index) => ({
x: new Date(date * 1000),
y: this.challenge[index]
}))
const ctx = document.getElementById([name]).getContext('2d')
let myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'Challenge',
data: this.data,
borderColor: ' #EA5455',
}
]
},
options: {
lineTension: 0,
maintainAspectRatio: false,
scales: {
yAxes: [
{
scaleLabel: {
display: false
},
ticks: {
beginAtZero: true,
callback (value) {
return `${value}%`
}
}
}
],
xAxes: [
{
type: 'time',
time: {
unit: 'month'
},
scaleLabel: {
display: true,
}
}
]
}
}
})
})
}
}
}
</script>
App.vue:
<template>
<div class="In order to display chart1">
<chart-display> <canvas slot="test1" id="chart_7" ></canvas> </chart-display>
</div>
<div class="In order to display chart1">
<chart-display> <canvas slot="test2" id="chart_8" ></canvas> </chart-display>
</div>
</template>
<script>
import chart-display from './Chart.vue'
export default {
component: {chart-display}
}
</script>
As you can see i have shared my Chart.vue and App.vue, i am able to see my chart in the browser, but whenever i run the code or refresh the page, the charts flickers and stops. And then in my console i get below error:
Please someone help me to get rid of this issue, and please tell me if any changes i should do in my code to solve it. Please send me the modification code.
As I wrote in my comment, the charts are rendered twice. This causes flickering.
// every time you use <chart-display>, 2 charts are rendered, this means chart 1 renders
// itself and chart 2, char 2 renders itself and chart 1, this is a bad pattern in Vue in general
mounted() {
this.check(8, "chart_8");
this.check(7, "chart_7");
}
Make the following changes:
ChartDisplay.vue
<template>
<div
class="chart-container"
style="position: relative; height: 40vh; width: 100%"
>
<canvas ref="chart_7"></canvas>
<canvas ref="chart_8"></canvas>
</div>
</template>
<script>
import Chart from "chart.js";
export default {
name: "ChartDisplay",
data() {
return {
date: [],
challenge: [],
data: [],
// save charts in an array
charts: [],
// charts options
options: {
lineTension: 0,
maintainAspectRatio: false,
scales: {
yAxes: [
{
scaleLabel: {
display: false,
},
ticks: {
beginAtZero: true,
callback(value) {
return `${value}%`;
},
},
},
],
xAxes: [
{
type: "time",
time: {
unit: "month",
},
scaleLabel: {
display: true,
},
},
],
},
},
};
},
mounted() {
this.render(7, this.$refs.chart_7);
this.render(8, this.$refs.chart_8);
},
methods: {
render(id, ctx) {
this.fetchData(id).then((response) => {
let data = response.date.map((date, index) => ({
x: new Date(date * 1000),
y: response.challenge[index],
}));
this.charts.push(
new Chart(ctx, {
type: "line",
data: {
datasets: [
{
label: "Challenge",
data: data,
borderColor: " #EA5455",
},
],
},
options: this.options,
})
);
});
},
fetchData(id) {
return this.$http.get(`/api_chart/${ id }/full`);
},
},
beforeDestroy() {
this.charts.forEach((chart) => chart.destroy());
},
};
</script>
<style >
[v-cloak] {
display: none;
}
</style>
App.vue
<template>
<div>
<div class="In order to display chart1">
<chart-display/>
</div>
</div>
</template>
<script>
import ChartDisplay from "./ChartDisplay.vue";
export default {
components: { ChartDisplay },
};
</script>
See it on sandbox
I found several errors on your code. I fix them in Sandbox
For Chat.vue :
I rename the file as ChartDisplay.vue as similar as the component name
import chart.js package for using Chart() function
I use a demo API
<template>
<div
class="chart-container"
style="position: relative; height: 40vh; width: 100%"
>
<slot name="test1"></slot>
<slot name="test2"></slot>
</div>
</template>
<script>
import Chart from "chart.js";
export default {
name: "ChartDisplay",
data() {
return {
date: [],
challenge: [],
data: [],
};
},
mounted() {
this.check(8, "chart_8");
this.check(7, "chart_7");
},
methods: {
check(id, name) {
fetch(
"https://api.wirespec.dev/wirespec/stackoverflow/fetchchartdataforvuejs"
)
.then((response) => response.json())
.then((response) => {
this.date = response.date;
this.challenge = response.challenge;
this.data = this.date.map((date, index) => ({
x: new Date(date * 1000),
y: this.challenge[index],
}));
const ctx = document.getElementById([name]).getContext("2d");
new Chart(ctx, {
type: "line",
data: {
datasets: [{
label: "Challenge",
data: this.data,
borderColor: " #EA5455",
}, ],
},
options: {
lineTension: 0,
maintainAspectRatio: false,
scales: {
yAxes: [{
scaleLabel: {
display: false,
},
ticks: {
beginAtZero: true,
callback(value) {
return `${value}%`;
},
},
}, ],
xAxes: [{
type: "time",
time: {
unit: "month",
},
scaleLabel: {
display: true,
},
}, ],
},
},
});
});
},
},
};
</script>
For App.vue
Your import should not carry any hyphen.
component should be components
render the component once to avoid flikering
<template>
<div>
<div class="In order to display chart1">
<chart-display>
<canvas slot="test1" id="chart_7"></canvas>
<canvas slot="test2" id="chart_8"></canvas>
</chart-display>
</div>
</div>
</template>
<script>
import ChartDisplay from "./ChartDisplay.vue";
export default {
components: {
ChartDisplay
},
};
</script>
I am using Vue-Chartjs to create a simple Line chart, I'm filling the chart with data via a get request to an API
however I want to generate new values randomly when I click on a button, & pass the values as a prop to chart-line component.
I've tried using reactiveProp & I also tried using a watcher for chartData prop, but I'm always getting this error
client.js?06a0:83 TypeError: Cannot read property 'map' of undefined
Dashboard Component
<template>
<div class="container">
<h1>Dashboard Page</h1>
<v-alert v-if="errorDetected"
class="mt-4"
dense
outlined
type="error"
>
There was an error while getting the chart data
</v-alert>
<v-btn #click="generateNewData()">Generate new data</v-btn>
<div class="loader-container">
<img v-if="!loaded" class="chart-loader mt-3" src="../static/loader-dotted.gif" alt="">
</div>
<ChartLine v-if="loaded" :chartData="values" :bind="true" />
</div>
</template>
<script>
import ChartLine from '../components/chart-line'
export default {
middleware: 'session',
components: {
ChartLine
},
data() {
return {
values: [],
customValues: [],
loaded: false,
errorDetected: false
}
},
head() {
return {
title: 'Dashboard page',
meta: [
{
hid: 'description',
name: 'description',
content: 'simple dashboard SPA'
}
]
}
},
mounted() {
this.requestData()
},
methods: {
requestData() {
this.loaded = false
this.$axios.get('http://www.mocky.io/v2/5eda474f330000fefc79eab4?mocky-delay=2000ms').then(response => {
console.log("requestData -> response", response)
this.values = response.data.data.value
this.loaded = true
}).catch(error => {
this.loaded = true
this.errorDetected = true
})
},
generateNewData() {
this.values = [];
for(let i=0; i<7; i++)
this.values.push(Math.floor((Math.random() * 10) + 1))
}
}
}
</script>
<style>
.loader-container {
display: flex;
justify-content: center;
}
.chart-loader {
width: 150px;
}
</style>
ChartLine Component
<script>
//Importing Line class from the vue-chartjs wrapper
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
//Exporting this so it can be used in other components
export default {
extends: Line,
mixins: [reactiveProp],
props: ['chartData'],
data () {
return {
datacollection: {
//Data to be represented on x-axis
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [
{
label: "Data 1",
backgroundColor: "transparent",
borderColor: "rgba(1, 116, 188, 0.50)",
pointBackgroundColor: "rgba(171, 71, 188, 1)",
//Data to be represented on y-axis
data: this.chartData
}
],
},
options: {
responsive: true,
maintainAspectRatio: false
}
//Chart.js options that controls the appearance of the chart
}
},
watch: {
chartData() {
this.renderChart(this.datacollection, this.options)
}
},
mounted () {
//renderChart function renders the chart with the datacollection and options object.
this.renderChart(this.datacollection, this.options)
}
}
</script>
UPDATE
I've managed to solve the issue of updating the chart
I removed the dataCollection from the chart-line component & added it in the dashboard component, I've also used the requestData() method in the created() hook to make a get request to the API, then on a button click I generate a new values and pass it as a prop
Update Code
Dashboard Component
<template>
<div class="container">
<h1>Dashboard Page</h1>
<v-alert v-if="errorDetected"
class="mt-4"
dense
outlined
type="error"
>
There was an error while getting the chart data
</v-alert>
<v-btn class="primary" #click="generateNewData()">Generate New Data</v-btn>
<div class="loader-container">
<img v-if="!loaded" class="chart-loader mt-3" src="../static/loader-dotted.gif" alt="">
</div>
<ChartLine v-if="loaded" :chart-data="dataCollection" />
</div>
</template>
<script>
import ChartLine from '../components/chart-line'
export default {
middleware: 'session',
components: {
ChartLine
},
data() {
return {
dataCollection: null,
values: [],
customValues: [],
loaded: false,
errorDetected: false
}
},
head() {
return {
title: 'Dashboard page',
meta: [
{
hid: 'description',
name: 'description',
content: 'simple dashboard SPA'
}
]
}
},
created() {
// this.loaded = false
// this.fillData()
// this.loaded = true
this.requestData()
},
methods: {
requestData() {
this.loaded = false
this.$axios.get('http://www.mocky.io/v2/5eda474f330000fefc79eab4?mocky-delay=2000ms').then(response => {
this.values = response.data.data.value
this.loaded = true
this.fillData()
}).catch(error => {
this.loaded = true
this.errorDetected = true
})
},
fillData () {
this.dataCollection = {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [
{
label: "Data 1",
backgroundColor: "transparent",
borderColor: "rgba(1, 116, 188, 0.50)",
pointBackgroundColor: "rgba(171, 71, 188, 1)",
data: this.values
}
]
}
},
generateNewData() {
this.values = []
this.loaded = false
setTimeout(function(){},2000)
for(let i=0; i<7; i++) {
this.values.push(Math.floor(Math.random() * (50 - 5 + 1)) + 5)
}
this.fillData()
this.loaded = true
}
}
}
</script>
<style>
.loader-container {
display: flex;
justify-content: center;
}
.chart-loader {
width: 150px;
}
</style>
Chart-Line Component
<script>
//Importing Line class from the vue-chartjs wrapper
import { Line, mixins } from 'vue-chartjs'
//Exporting this so it can be used in other components
export default {
extends: Line,
mixins: [mixins.reactiveProp],
// props:['chartData'],
data () {
return {
options: {
responsive: true,
maintainAspectRatio: false
}
//Chart.js options that controls the appearance of the chart
}
},
mounted () {
//renderChart function renders the chart with the datacollection and options object.
this.renderChart(this.chartData, this.options)
}
}
</script>
however there's still one thing I can't figure out, which is the loading state, when clicking on the button to generate new data
when I first open the dashboard page, the loading state works, but when I click on the button, loading state doesn't work
any Idea why??????