Cannot render multiple times the same amchart3 graph in a vue component - vue.js

I'm using amcharts3 to generate graphs, Bootstrap 4 for the style and Vue2 for the components. I have a parent component which shows a donut chart which is a component on its own, and I want to render those charts many times as I need
Parent component
<template>
<div class="w-100 container-fluid">
<div class="row">
<!-- Here it goes the Donut Charts -->
</div>
</div>
</template>
<script>
import donutChart from './donutChart';
export default {
components:{
'donut-chart': donutChart
},
data(){
return{
graficos: [
{id: 'grafico', valor:100, valor2: 0},
{id: 'dona', valor:75, valor2: 25},
{id: 'homero', valor:50, valor2:50},
]
}
}
}
</script>
<style></style>
donutChart.vue (the amcharts3 library is already loaded in the Vue project)
<template>
<div class="col">
<div :id="this.dato.id"></div>
</div>
</template>
<script>
export default {
props:{
dato: {
type: Object,
default: {id: 'chartdiv', valor:50, valor2:50}
}
},
data() {
return {
}
},
created: function(){
console.log(this.dato)
AmCharts.makeChart( this.dato.id, {
"type": "pie",
"theme": "none",
"responsive": {
"enabled": true
},
"minWidth": 200,
"maxWidth": 400,
"dataProvider": [ {
"title": "et0",
"value": this.dato.valor
},
{
"title": "Restante",
"value": this.dato.valor2
} ],
"startDuration": 0,
"titleField": "title",
"valueField": "value",
"labelRadius": 5,
"radius": "40%",
"innerRadius": "80%",
"allLabels": [{
"y": "46%",
"align": "center",
"size": 14,
"text": "50%",
"color": "#555"
}]
});
},
mounted: function(){
},
methods:{
}
}
</script>
<style></style>
The problem is the donutChart.vue component is rendered in the parent, and it shows the chart
<donut-chart></donut-chart>
But when I try to render multiple times the same component and/or passing data via props the chart doesn't render at all
<donut-chart :dato="{id: 'grafico', valor: 100, valor2: 0}"></donut-chart>
<donut-chart :dato="{id: 'dona', valor: 75, valor2: 25}"></donut-chart>
<donut-chart :dato="{id: 'homero', valor: 50, valor2 :50}"></donut-chart>
I'm doing something wrong?

Well. I've managed to see what's going on and it looks so stupid. In donutChart.vue thereĀ“s an style that only applies to #chartdiv element, I didn't post in the question because I didn't figure that code until now. Now I'm applying the same style for every component and now the charts are renderized

Related

Creating a custom component Vue3js

I downloaded the DataTable component vue3-easy-data-table. Then I created a custom component where I define the theme for the table.
Since the easy-data-table uses slots to operate on the table items, how can I redirect my template tags to the table instead of my custom component?
<script>
export default {
setup() {
const themeColor = getComputedStyle(document.documentElement).getPropertyValue('--primary');
return { themeColor }
}
}
</script>
<template>
<DataTable table-class-name="table-theme" :theme-color="themeColor" alternating>
<template #expand="item">
<slot name="expand" v-bind="item"></slot>
</template>
</DataTable>
</template>
<style lang="scss" scoped>
.table-theme {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
This is my custom component. At the moment I manually created a copy template for the expand function and it works alright, but not only it looks ugly and redundant, it also won't work to access specific items.
Do you have a solution?
I found a solution. The slots object that you get in your setup function will contain the "parent requested slot" even if they are not defined in the template.
Therefore you can easily pass the slots names to the template, iterate through them and automatically generate the needed slots.
<script>
export default {
setup(props, { slots }) {
const themeColor = getComputedStyle(document.documentElement).getPropertyValue('--primary');
const slotNames = Object.keys(slots)
return { themeColor, slotNames }
}
}
</script>
<template>
<DataTable table-class-name="table-theme" :theme-color="themeColor" alternating>
<template v-slot:[slot]="model" v-for="slot in slotNames">
<slot :name="slot" v-bind="model"></slot>
</template>
</DataTable>
</template>
You don't really need to wrap the table with a custom component.
But unfortunately the vue3-easy-data-table does not update with changing the themeColor.
So, the quick and dirty solution is to force the table recreate, by destroying it using v-if.
Here is the playground (check it in the full page)
const App = {
components: {
EasyDataTable: window['vue3-easy-data-table'],
},
data () {
return {
themeColor: "#f48225",
headers:[
{ text: "Name", value: "name" },
{ text: "Age", value: "age", sortable: true }
],
items: [
{ "name": "Curry", "height": 178, "weight": 77, "age": 20 },
{ "name": "James", "height": 180, "weight": 75, "age": 21 },
{ "name": "Jordan", "height": 181, "weight": 73, "age": 22 }
],
}
},
methods: {
resetTable() {
this.themeColor = null;
}
}
};
Vue.createApp(App).mount('#app');
<div id="app">
Theme Color: {{themeColor}}<br /><br />
<input type="radio" v-model="themeColor" value="#f48225" #input="resetTable"/> #f48225
<input type="radio" v-model="themeColor" value="#123456" #input="resetTable" /> #123456 <br /><br />
<easy-data-table v-if="themeColor != null"
:headers="headers"
:items="items"
:theme-color="themeColor"
buttons-pagination
alternating
/>
</div>
<link href="https://unpkg.com/vue3-easy-data-table/dist/style.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#3.2.1/dist/vue.global.js"></script>
<script src="https://unpkg.com/vue3-easy-data-table"></script>

How to dynamically insert text in Ckeditor5 for Vue 2

My application provides many selectable standard paragraphs which users can insert at any point. In a previous version insertion was simple: CKEDITOR.instances[editorId].insertHTML(text).
I'm trying to use v5 api with model.insertContent(text) but get error:
"vue.runtime.esm.js:3020 TypeError: t2.is is not a function
at bl.rp (converters.js:771:21)
at bl.fire (emittermixin.js:199:30)
at <computed> (observablemixin.js:262:16)
at VueComponent.testInsert (Test.vue:29:1)"
Here's the composition file. I have tried accessing the insertContent method from this.editor and from the apiReference returned from the #ready event;
<template>
<div>
<button #click="testInsert('abracadabra')">Insert literal content</button>
<ckeditor tag-name="textarea" :editor="editor" v-model="doc.HTML" #ready="editorReady" :config="editorConfig"></ckeditor>
</div>
</template>
<script>
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
export default {
name: 'testEditorInsert',
mounted() {
},
data() {
return {
doc: {ID: 0, HTML: "This is a sample text. Insert here? This is the remainder of the sample text"},
editor: ClassicEditor,
editorData: 'loading',
editorConfig: {
toolbar: [ 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote' ],
},
editorApi:null ,
}
},
methods: {
testInsert: function(text) {
console.log(this.editorApi, text);
//this.editor.model.insertContent(text);
this.editorApi.model.insertContent(text);
},
editorReady: function(editor) {
this.editorApi = editor;
}
},
}
</script>

Bar chart from Vue-chartjs doesn't render

In my Vue project I cannot render the Bar Chart I've created using vue-chartjs
I've tried reinstalling vue-chartjs and update my component, but nothing work.
I wonder if it's the problem with the component itself or I do rendering in a wrong way?
BarChart component
<script>
import { Bar } from "vue-chartjs";
export default {
extends: Bar,
mounted() {
this.renderChart(
{
labels: [
"London",
"New York",
"New Zeland",
],
datasets: [
{
label: "Data One",
backgroundColor: "#f87979",
data: [
83,
26,
4,
]
}
]
},
{ responsive: true, maintainAspectRatio: false, height: 200 }
);
}
};
</script>
Home component:
<template>
<v-container class="grey lighten-5">
<v-col cols="12" sm="8">
<v-card class="pa-2" outlined align="center">
<BarChart/>
<div>CHART</div>
</v-card>
</v-col>
</v-container>
</template>
<script>
import BarChart from "#/components/charts/BarChart";
export default {
components: {
BarChart
},
};
</script>
<style></style>
The mistake in the method this.renderChart(). I had to look at the documentation to figure out how it should be structured.
The method this.renderChart() is provided by the Bar component and accepts two parameters: both are objects. The first one is your chart data, and the second one is an options object.
More in docs - https://www.chartjs.org/docs/latest/charts/bar.html
This worked for me:
<script>
import { Bar } from "vue-chartjs";
export default {
extends: Bar,
mounted() {
this.renderChart(
{
labels: [
"London",
"New York",
"New Zeland",
],
datasets: [
{
label: "Data One",
backgroundColor: "#f87979",
data: [
83,
26,
4,
]
}
]
},
{ responsive: true }
);
}
};
</script>

how to add correctly an image in vuejs

I want to have the image be shown in my project; however, I wasn't able to work it out.
I did search for a solution, and I tried it but it didn't work out for me.
What could be the solution here? Thank you in advance for your help.
Here is the html part:
<template>
<div id="app">
<div class="productListing">
<div class="box effect1" :key="product.id" v-for="product in ProductFilter.slice(0,3)">
<h1>{{product.title}}</h1>
<img :src="product.img" alt="dd">
</div>
</div>
</div>
</template>
vue js part
<script>
export default {
data: function () {
return {
userFilterKey: 'all',
products:[
{
id: 1,
title: "Google Pixel - Black",
price: 10,
company: "GOOGLE",
status : "TopSell",
img: "../../bg.jpeg"
}
],
}
},
computed: {
ProductFilter() {
return this[this.userFilterKey]
},
all() {
return this.products
},
TopSell() {
return this.products.filter((product) => product.status === "TopSell")
},
highRated() {
return this.products.filter((product) => product.status === "HighRated")
},
}
}
// # is an alias to /src
</script>
Thanks again in advance for your help.
Assuming the image path you provided is correct, use the require function instead.
products: [
{
id: 1,
title: "Google Pixel - Black",
price: 10,
company: "GOOGLE",
status : "TopSell",
img: require("../../bg.jpeg")
}
],

Trigger child component method from parent event

I am very new to VueJS and JavaScript and your help would be much appreciated.
My method "greet" is not working and I am unsure why. When I click on the button "change to bar" I get the error:
[Vue warn]: Property or method "greet" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
found in
---> <Chatjsvue> at src\components\vueChartjs\Chatjsvue.vue
<App> at src\App.vue
<Root>
chartjs.vue
<template src="../../views/chartjshtml/chartsjs.html"></template>
<script>
import LineChart from '#/assets/javascripts/chartjsline'
export default {
components: {
'line-chart': LineChart
}
}
</script>
chartsjs.html
<div class="wrapper">
<div>
<ul>
<li><button v-on:click="greet()">change to bar</button></li>
<li><line-chart></line-chart></li>
</ul>
</div>
</div>
chartsjsline.js
import { Line } from 'vue-chartjs'
export default {
extends: Line,
data() {
return {
datacollection: {
//Data to be represented on x-axis
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
datasets: [{
label: 'Activities ChartJS Line',
backgroundColor: '#f87979',
pointBackgroundColor: 'white',
borderWidth: 1,
pointBorderColor: '#249EBF',
//Data to be represented on y-axis
data: [40, 20, 30, 50, 90, 10, 20, 40, 50, 70, 90, 100]
}]
},
//Chart.js options that controls the appearance of the chart
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
display: true
}
}],
xAxes: [{
gridLines: {
display: false
}
}]
},
legend: {
display: true
},
responsive: true,
maintainAspectRatio: false
},
}
},
methods: {
greet() {
alert('hello');
}
},
mounted() {
//renderChart function renders the chart with the datacollection and options object.
this.renderChart(this.datacollection, this.options)
}
}
Your <line-chart> component (instance of LineChart) is a child component of your Chatjsvue component.
The child methods remain bound to those instances.
However it is extremely easy from the parent component to access its child component, and from there to execute their methods:
Keep a reference to your child component, using the ref special attribute: <line-chart ref="myLineChart"></line-chart>
Within a parent method, access the referred child using this.$refs.myLineChart.
Then you have access to everything on this child instance, including its methods: this.$refs.myLineChart.greet()
Working example:
Vue.component('chart-js', {
template: '#chartjs',
methods: {
lineChildGreet() {
// Access the child component through $refs.
// Then you can execute its methods.
this.$refs.myLineChart.greet();
},
},
});
Vue.component('line-chart', {
template: '#lineChart',
methods: {
greet() {
alert('hello');
},
},
});
var app = new Vue({
el: '#app',
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<chart-js></chart-js>
</div>
<template id="chartjs">
<div class="wrapper">
<div>
<ul>
<li><button v-on:click="lineChildGreet">greet on child component</button></li>
<!-- Keep a REFerence to the child component -->
<li><line-chart ref="myLineChart"></line-chart></li>
</ul>
</div>
</div>
</template>
<template id="lineChart">
<span>LineChart</span>
</template>
You need to add the greet() method to the chartjs.vue component, instead of chartsjsline.