How can I change my c3 chart axis color in vue environment - vue.js

I want use c3.js in my vue app, it's successful to build a chart but I can't change my c3 chart axis color.
It's not just axis color, all style of c3 charts I can't change and fixed, it's upset for me.
Do you have any idea for it?
<template>
<div id="home">
<div class="block1">
<div id='lineChart'></div>
</div>
</div>
</template>
<script>
import * as d3 from "d3";
import c3 from 'c3';
export default {
mounted(){
this.loadBlock1();
},
methods:{
loadBlock1 () {
let chart = c3.generate({
bindto:'#lineChart',
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
],
}
});
}
}
}
</script>
<style scoped>
#import "https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.0/c3.min.css";
#lineChar .c3-line-data2 {
stroke-width: 5px;
}
#lineChart .c3-axis-x line,
#lineChart .c3-axis-x path {
stroke:blue;
}
</style>

Your code is not working because when you use <style scoped> Vue emulates shadow DOM adding a hash on the DOM elements and on the CSS selectors. The elements created by c3 do not contain the hash and the css selector do not match with them. So, add a style tag without the scoped attr for styling the chart.
<template>...</template>
<script>...</script>
<style scoped>...</style>
<style>...</style>
Also, avoid getting the DOM element using query selectors. Vue uses VDOM approach and DOM access should be done through directives and refs:
Vue.directive("c3", {
bind(el, binding, vnode) {
const chart = c3.generate({
bindto: el,
data: binding.value,
});
el.chart = chart;
},
update(el, binding, vnode) {
el.chart.resize();
},
unbind(el, binding, vnode) {
el.chart.destroy();
}
});
const home = new Vue({
el: "#home",
data() {
return {
options: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
],
},
};
},
computed: {
chart() {
return this.$refs.chart.chart;
}
},
methods: {
load(data) {
this.chart.load(data);
},
unload(data) {
this.chart.load(data);
}
},
mounted() {
this.chart.resize();
}
});
setTimeout(() => {
home.load({
columns: [
['data1', 230, 190, 300, 500, 300, 400]
]
});
}, 1000);
setTimeout(function() {
home.load({
columns: [
['data3', 130, 150, 200, 300, 200, 100]
]
});
}, 1500);
setTimeout(function() {
home.unload({
ids: 'data1'
});
}, 2000);
#home .c3-line-data2 {
stroke-width: 5px;
}
#home .c3-axis-x line,
#home .c3-axis-x path {
stroke: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.0/c3.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.0/c3.min.js"></script>
<div id="home">
<div class="block1">
<div ref="chart" v-c3="options"></div>
</div>
</div>

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>

Recursive component view does not display after data change

A common need for data binding is manipulating an element's class list and inline styles. Since class and style are both attributes, we can use v-bind to assign them a string value dynamically, much like with other attributes.
I want to have 2d array with turned on and off dates checked in component by v-if
interesting variable is dir.
<html>
<body>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div class="container">
<h4>
Vue.js Expandable Tree Menu<br />
<small>(Recursive Components)</small>
</h4>
<div id="app">
<tree-menu :nodes="tree.nodes" :depth="0" :label="tree.label" :dir="dir"></tree-menu>
</div>
</div>
<script>
let tree = {
label: "root",
nodes: [
{
label: "item1",
nodes: [
{
label: "item1.1",
},
{
label: "item1.2",
nodes: [
{
label: "item1.2.1",
},
],
},
],
},
{
label: "item2",
},
],
};
Vue.component("tree-menu", {
props: ["nodes", "label", "depth", "dir"],
template: `
<div >
<div class="label-wrapper" #click="toggleChildren">
<div :style="indent" :class="labelClasses">
<i v-if="nodes" class="fa" :class="iconClasses"></i>
{{ label }}
</div>
</div>
<tree-menu
v-if="dir[0][0]"
v-for="node in nodes"
:nodes="node.nodes"
:label="node.label"
:depth="depth + 1"
:dir="dir"
>
</tree-menu>
</div>`,
data() {
return {
showChildren: false,
};
},
computed: {
iconClasses() {
return {
"fa-plus-square-o": !this.showChildren,
"fa-minus-square-o": this.showChildren,
};
},
labelClasses() {
return { "has-children": this.nodes };
},
indent() {
return { transform: `translate(${this.depth * 50}px)` };
},
},
methods: {
toggleChildren() {
this.dir[0][0] = !this.dir[0][0];
alert(this.dir[0][0]);
},
},
});
new Vue({
el: "#app",
data: {
tree,
dir: [
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
],
},
});
</script>
<style>
body {
font-family: "Open Sans", sans-serif;
font-size: 18px;
font-weight: 300;
line-height: 1em;
}
.container {
width: 300px;
margin: 0 auto;
}
.tree-menu {
.label-wrapper {
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
.has-children {
cursor: pointer;
}
}
}
</style>
</body>
</html>
I want to have 2d array with turned on and off dates that are checked by v-if.

Converting Vue Component to Vuetify Component

I am using Vue+Vuetify. Never learnt Vue standlone went straight into the duo
I am trying to re-create a sparkline metric - Can be seen in this codepen "PageViews" (Source starts at line 30 in JS)
The issue: Vue standalone has a different method of registering components. I have attempted to re-jig the code and register the component according to Vuetify's rules. Normally a sparkline in Vuetify is simply called with <v-sparkline/>.
Despite my efforts I am stuck with the error: TypeError: "this.Chart is not a function".
What am I doing wrong?
My attempt: metric.vue
<template>
<div class="br2">
<div class="pa3 flex-auto bb b--white-10">
<h3 class="mt0 mb1 f6 ttu white o-70">{{ title }}</h3>
<h2 class="mv0 f2 fw5 white">{{ value }}</h2>
</div>
<div class="pt2">
<canvas></canvas>
</div>
</div>
</template>
<script>
export default {
props: ["title", "value"],
data () {
return {
ctx: null,
}
},
mounted () {
this.ctx = this.$el.querySelector("canvas").getContext("2d");
let sparklineGradient = this.ctx.createLinearGradient(0, 0, 0, 135);
sparklineGradient.addColorStop(0, "rgba(255,255,255,0.35)");
sparklineGradient.addColorStop(1, "rgba(255,255,255,0)");
let data = {
labels: ["A", "B", "C", "D", "E", "F"],
datasets: [{
backgroundColor: sparklineGradient,
borderColor: "#FFFFFF",
data: [2, 4, 6, 4, 8, 10]
}]
};
this.Chart(this.ctx, {
data: data,
options: {
elements: {
point: {
radius: 0
}
},
scales: {
xAxes: [{
display: false
}],
yAxes: [{
display: false
}]
}
}
});
}
}
</script>
you did not import Chart from chart.js
<template>
<div id="app">
<div class="br2">
<div class="pt2">
<canvas></canvas>
</div>
</div>
</div>
</template>
<script>
import Chart from 'chart.js'
export default {
name: "App",
data(){
return{
ctx: null
}
},
created: function() {
Chart.defaults.global.legend.display = false;
},
mounted: function() {
this.ctx = this.$el.querySelector("canvas").getContext("2d");
let sparklineGradient = this.ctx.createLinearGradient(0, 0, 0, 135);
sparklineGradient.addColorStop(0, "rgba(255,255,255,0.35)");
sparklineGradient.addColorStop(1, "rgba(255,255,255,0)");
let data = {
labels: ["A", "B", "C", "D", "E", "F"],
datasets: [{
backgroundColor: 'red',
borderColor: "#FFFFFF",
data: [2, 4, 6, 4, 8, 10]
}]
};
Chart.Line(this.ctx, {
data: data,
options: {
elements: {
point: {
radius: 0
}
},
scales: {
xAxes: [{
display: false
}],
yAxes: [{
display: false
}]
}
}
});
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

My html code can't reach my vue component

This is my second day with vue. I was already using webpack and vue-cli, but I would like to understand how to make everything working within one file. I developed a code which works well, but I would like to refactor the code to have a component which I could later use to generate screen full of color changing tiles.
I tried Vue.component('name', {}), but with no result, because in the console I'm seeing [Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions. and [Vue warn]: Unknown custom element: <brick> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
This code works well:
<html>
<head>
<title>v pavle</title>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
template:
'<div v-bind:style="styleobj" v-on:mouseover="changebgcolor" v-on:mouseout="changebgcolor"></div>',
data: {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
},
methods: {
changebgcolor: function() {
this.styleobj.backgroundColor = Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
</script>
</body>
</html>
And that code gives everything, but not what I want to see :(
<html>
<head>
<title>v pavle</title>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id="app">
<brick></brick>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app"
});
var brick = Vue.component("brick", {
template:
'<div v-bind:style="styleobj" v-on:mouseover="changebgcolor" v-on:mouseout="changebgcolor"></div>',
data: {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
},
methods: {
changebgcolor: function() {
this.styleobj.backgroundColor = Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
</script>
</body>
</html>
It may seem easy for you, but after 7h spent, there is nothing more for me, but just ask you on SO
Okay I will answer your 2 questions. First and about data, it has to be a function. So you have to write it like that:
data() {
return {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
}
}
After that, your forgot to reference your component in your Vue instance. Try that:
var vm = new Vue({
el: "#app",
components: {
brick: brick
}
})
Hope it will work.
Data must be a function like data: function(){ return obj }
Register the component using components: {yourcomponent}
You needed to use # in front of your color.
<html>
<head>
<title>v pavle</title>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id="app">
Hello App!
<brick>Hello Brick</brick>
</div>
<script type="text/javascript">
var brick = Vue.component("brick", {
template:
'<div :style="styl" #click="changebgcolor" #mouseover="changebgcolor" #mouseout="changebgcolor"><slot></slot></div>',
data: function(){
return {
styl: {
width: "100px",
height: "100px",
backgroundColor: "#b6d8a1",
color: "blue"
}
};
},
methods: {
changebgcolor: function() {
console.log('changebgcolor!');
this.styl.backgroundColor = "#"+ Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
var vm = new Vue({
el: "#app",
components:{brick:brick}
});
</script>
</body>
</html>
When using Vue.component, you need to make sure that you have registered all components once you start rendering your app. At the moment, you first render the main app and then register the component, so swap those around
var brick = Vue.component("brick", {
template:
'<div v-bind:style="styleobj" v-on:mouseover="changebgcolor" v-on:mouseout="changebgcolor"></div>',
data: {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
},
methods: {
changebgcolor: function() {
this.styleobj.backgroundColor = Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
var vm = new Vue({
el: "#app"
});
The data property on your component should be a function that returns an object. Why? Because otherwise all instances of your component share the same data, meaning all tiles on your app would have the same color.
data() {
return {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
}
},
You should first register the component before you fire up the Vue instance, just reorder your code and it works!

Vue: Change class with the value of a variable in setInterval

I am learning Vue JS. I want to change class using setInterval. But can’t pass the changing value of Method to Computed Property. After two seconds class will change automatically with the changed value of "changeColor"
My Code:
HTML:
<div>
<button #click="startEffect">Start Effect</button>
<div id="effect" :class="myClass"></div>
</div>
CSS:
.highlight {
background-color: red;
width: 200px !important;
}
.shrink {
background-color: gray;
width: 50px !important;
}
Vue JS:
new Vue({
el: '#exercise',
data: {
changeColor: false
},
methods : {
startEffect: function() {
setInterval(function(){
this.changeColor = !this.changeColor;
//alert(this.changeColor);
}, 2000);
//alert(this.changeColor);
}
},
computed: {
myClass: function() {
return {
highlight: this.changeColor,
shrink: !this.changeColor
}
}
}
})
bind your function to the component...
setInterval(function(){
this.changeColor = !this.changeColor;
}.bind(this), 2000);
and then you can do ...
<div id="effect" :class="[changeColor?'highlight':'shrink']"></div>