Related
I am working on a project where I am implementing some charts from the Vue-Chartjs library. I need the Y-axis max value to change everytime the user changes the filters given. I Import an existing barchart from the vue-chartjs library. In the code there is a javascript file that has some defaults already, to set extra options I can use the extraOptions object as a prop to personalize each chart accordingly. Here is the default component:
import { Bar } from 'vue-chartjs'
import { hexToRGB } from "./utils";
import reactiveChartMixin from "./mixins/reactiveChart";
let defaultOptions = {
tooltips: {
tooltipFillColor: "rgba(0,0,0,0.5)",
tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
tooltipFontSize: 14,
tooltipFontStyle: "normal",
tooltipFontColor: "#fff",
tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
tooltipTitleFontSize: 14,
tooltipTitleFontStyle: "bold",
tooltipTitleFontColor: "#fff",
tooltipYPadding: 6,
tooltipXPadding: 6,
tooltipCaretSize: 8,
tooltipCornerRadius: 6,
tooltipXOffset: 10,
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
fontColor: "#9f9f9f",
fontStyle: "bold",
beginAtZero: true,
display: false,
min: 0,
max: 100
},
gridLines: {
display: false,
drawBorder: false,
}
}],
xAxes: [{
gridLines: {
display: false,
drawBorder: false,
},
}],
}
};
export default {
name: 'BarChart',
extends: Bar,
mixins: [reactiveChartMixin],
props: {
labels: {
type: [Object, Array],
description: 'Chart labels. This is overridden when `data` is provided'
},
datasets: {
type: [Object, Array],
description: 'Chart datasets. This is overridden when `data` is provided'
},
data: {
type: [Object, Array],
description: 'Chart.js chart data (overrides all default data)'
},
color: {
type: String,
description: 'Chart color. This is overridden when `data` is provided'
},
extraOptions: {
type: Object,
description: 'Chart.js options'
},
title: {
type: String,
description: 'Chart title'
},
},
methods: {
assignChartData() {
let { gradientFill } = this.assignChartOptions(defaultOptions);
let color = this.color || this.fallBackColor;
return {
labels: this.labels || [],
datasets: this.datasets ? this.datasets : [{
label: this.title || '',
backgroundColor: gradientFill,
borderColor: color,
pointBorderColor: "#FFF",
pointBackgroundColor: color,
pointBorderWidth: 2,
pointHoverRadius: 4,
pointHoverBorderWidth: 1,
pointRadius: 4,
fill: true,
borderWidth: 1,
data: this.data || []
}]
}
},
assignChartOptions(initialConfig) {
let color = this.color || this.fallBackColor;
const ctx = document.getElementById(this.chartId).getContext('2d');
const gradientFill = ctx.createLinearGradient(0, 170, 0, 50);
gradientFill.addColorStop(0, "rgba(128, 182, 244, 0)");
gradientFill.addColorStop(1, hexToRGB(color, 0.6));
let extraOptions = this.extraOptions || {}
return {
...initialConfig,
...extraOptions,
gradientFill
};
}
},
mounted() {
this.chartData = this.assignChartData({});
this.options = this.assignChartOptions(defaultOptions);
this.renderChart(this.chartData, this.options, this.extraOptions);
}
}
I use this js file to import the bar chart inside a vue component like you see down below.
everytime the input of the form changes i need to re render the chart. I use the onInputChange() method to turn the boolean loaded to false and call the loadData() method.
Inside the loadData() method I make an axios request that gets me the right data every time. I also get the maximum value for my Y axis.
Then in the response I call on updateChart() so that I can update the data and the max value of the chart. then i turn the boolean loaded to true again so that my chart renders accordingly.
The problem with this approach is that the chart disappears completely for a split of a second. Before deciding to change the max Value of the Y axis I was able to update the data of my chart without having to use the v-if="loaded".
I need to find a solution where the chart re renders without it completely disappearing from the page. I know some suggested to use computed variables but i don't fully understand how it is supposed to work. Here is the component minus the form fields.
I guess in it's essence what I want is to update the Y axis max value without having to re render the entire chart.
<template>
<div>
<BarChart v-if="loaded" :labels="chartLabels"
:datasets="datasets"
:height="100"
:extraOptions="extraOptions"
>
</BarChart>
<br>
</div>
</template>
<script>
import BarChart from '../../components/Library/UIComponents/Charts/BarChart'
import Dropdown from "../../components/Library/UIComponents/Dropdown"
import GroupedMultiSelectWidget from "~/components/widgets/GroupedMultiSelectWidget"
import SelectWidget from "../../components/widgets/SelectWidget";
export default{
name: 'PopularChart',
components: {BarChart, Dropdown, SelectWidget, GroupedMultiSelectWidget},
data(){
return {
loaded:true,
form:{
day: 'Today',
workspace:'',
machine_family: [],
duration: [],
user_group: [],
dt_start:'',
dt_end:''
},
url: `/api/data_app/job_count_by_hour/`,
chart_data: [],
days: [ {day:"Today", id:"Today"},
{day:"Monday", id:"0"},
{day:"Tuesday",id:"1"},
{day:"Wednesday",id:"2"},
{day:"Thursday",id:"3"},
{day:"Friday",id:"4"},
{day:"Saturday",id:"5"},
{day:"sunday",id:"6"} ],
chartLabels: ["00u", "1u", "2u", "3u","4u","5u", "6u", "7u", "8u", "9u", "10u", "11u", "12u", "13u", "14u", "15u","16u", "17", "18u","19u","20u","21u","22u","23u"],
datasets: [],
maximumValue: '',
extraOptions:{}
}
},
methods: {
onInputChange() {
this.loaded = false
this.loadData()
},
async loadData() {
await this.$axios.get(`${this.url}?day=${this.form.day}&date_start=${this.form.dt_start}&date_end=${this.form.dt_end}&workspace=${this.form.workspace}&user_group=${this.form.user_group}&machine_family=${this.form.machine_family}`)
.then(response => {
this.updateChart(response.data.results,response.data.maximum)
this.loaded = true
})
},
updateChart(data,maxValue) {
this.datasets = [{
label: ["jobs %"],
backgroundColor:"#f93232",
data: data
},]
this.maximumValue = maxValue
this.extraOptions = {
tooltips: {
callbacks:{
label: function (tooltipItems,){
if (tooltipItems.value > ((50/100) * maxValue)){
return 'busy';
}else if (tooltipItems.value < ((30/ 100) * maxValue) ){
return ' not busy';
}else if ( tooltipItems.value < ((40/ 100) * maxValue )){
return 'kind of busy'
}
}
}
},
scales: {
yAxes: [{
gridLines: {
zeroLineColor: "transparent",
display: false,
drawBorder: false,
},
ticks: {
max: this.maximumValue,
display: true,
}
}],
xAxes: [{
gridLines: {
zeroLineColor: "transparent",
display: false,
drawBorder: false,
},
}],
},
}
},
},
mounted() {
this.loadData()
},
}
</script>
After checking your code, I noticed that you are using the datasets and maximumValue in data function.
To update the chart data based on dataset and maximumValue, you need to use those variables in computed data, not data.
For example,
computed: {
chartData() {
let chartData = {
labels: [],
datasets: [...],
}
return chartData;
},
maximumValue() {
return this.maxValue;
}
},
methods: {
renderBarChart() {
this.renderChart(this.chartData, {
legend: {
display: false,
},
responsive: true,
maintainAspectRatio: false,
options: {
scales: {
yAxes: [{
ticks: {
max: this.maximumValue
}
}],
},
}
});
},
},
I use CanvasJS. Whenever the chart contains multiple dataSeries, it is recommended to represent each dataSeries in a legend(in jsfiddel ANS1,ANS2 is legend name) and hovers on a dataPoint or dataSeries, a toolTip appears with information about the dataPoint and dataSeries. when i click legend each then hide the data set and remove graph bar but not on hover tooltip.
1. 1st Image is correct because 2 tooltip and 2 dataset
when i click ANC1 it hide but tooltip still show.
2. 2nd Image is incorrect because still 2 tooltip and 1 dataset
My Code in
jsfiddle
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "title"
},
axisX:{
title: "xxxxxxxxxx",
labelAngle: 135,
labelFontSize: 23
},
axisY:{
title:"Number of Services"
},
axisY2: {
title: "Number of Eligible Couple",
titleFontSize: 25,
},
toolTip: {
enabled: true,
shared: true },
animationEnabled: true,
legend:{
horizontalAlign: "center", // left, center ,right
verticalAlign: "top", // top, center, bottom
cursor:"pointer",
itemclick: function(e) {
if (typeof (e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
e.dataSeries.visible = false;
}
else
{
e.dataSeries.visible = true;
}
chart.render();
}
},
data: [
{
type: "column",
name: "ANC1",
showInLegend: "true",
visible: true,
dataPoints: [{label:"ROY2",x:0,y:36},{label:"ROY3",x:1,y:36}]
},
{
type: "column",
name: "ANC2",
showInLegend: "true",
visible: true,
dataPoints: [{label:"ROY2",x:0,y:56},{label:"ROY3",x:1,y:36}]
}
]
});
chart.render();
You can set up a custom tooltip https://canvasjs.com/docs/charts/chart-options/tooltip/content-formatter/
Code for your case:
var chart = new CanvasJS.Chart("chartContainer",
{
toolTip: {
enabled: true,
shared: true,
contentFormatter: function(e){
var str = "";
for (var i = 0; i < e.entries.length; i++){
if(e.entries[i].dataSeries.visible){
var temp = (e.entries[i].dataSeries.name + ': '+ e.entries[i].dataPoint.y);
str = str.concat(temp, '<br>');
}
};
return (str);
}
},
...
I want to show only one chart after the page load and then you can select a chart in the dropdown menu. The issues is when I add the class display:none; the graph won't load when selected in the dropdown.
How can I solve this?
<select id='chart-graph-progress'>
<option value="revenue-opt">Revenue</option>
<option value="rpu-opt">Revenue per user</option>
</select>
<div class="card2 full-chart-topmargin" id='revenue'>
<div class="big-text1-blue text-center">
Revenue
</div>
<div class="card-block">
<div class="chart-wrapper fullsize">
<canvas id="revenue-chart"></canvas>
</div>
</div>
</div>
<div style="display:none;" class="card2 full-chart-topmargin" id='rpu'>
<div class="big-text1-blue text-center">
Revenue per user
</div>
<div class="card-block">
<div class="chart-wrapper fullsize">
<canvas id="rpu-chart"></canvas>
</div>
</div>
</div>
Here is my custom.js file.
$(document).ready(function(){
$('#chart-graph-progress').on('change', function() {
if ( this.value == 'revenue-opt')
{
$("#revenue").show();
}
else
{
$("#revenue").hide();
}
});
$('#chart-graph-progress').on('change', function() {
if ( this.value == 'rpu-opt')
{
$("#rpu").show();
}
else
{
$("#rpu").hide();
}
});
});
Chart.js
var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
var lineChartData = {
labels : ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
datasets : [
{
label: 'Revenue',
labelColor : '#fff',
fontColor : '#fff' ,
backgroundColor : 'rgba(220,220,220,0.2)',
borderColor : 'rgba(220,220,220,1)',
pointBackgroundColor : 'rgba(220,220,220,1)',
pointBorderColor : '#fff',
data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
}
]
};
var options = {
maintainAspectRatio: false,
legend: {
display: false,
},
scales: {
xAxes: [{
gridLines: {
display: false,
color: '#03A5C5',
lineWidth: 8,
},
ticks: {
fontColor: "white",
},
}],
yAxes: [{
gridLines: {
display: false,
color: '#03A5C5',
lineWidth: 8,
},
ticks: {
fontColor: "white",
beginAtZero: true,
}
}]
}
};
var ctx = document.getElementById('revenue-chart');
var chart = new Chart(ctx, {
responsive: true,
type: 'line',
data: lineChartData,
options: options
});
var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
var lineChartData = {
labels : ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
datasets : [
{
label: 'Revenue',
labelColor : '#fff',
fontColor : '#fff' ,
backgroundColor : 'rgba(220,220,220,0.2)',
borderColor : 'rgba(220,220,220,1)',
pointBackgroundColor : 'rgba(220,220,220,1)',
pointBorderColor : '#fff',
data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
}
]
};
var options = {
maintainAspectRatio: false,
legend: {
display: false,
},
scales: {
xAxes: [{
gridLines: {
display: false,
color: '#03A5C5',
lineWidth: 8,
},
ticks: {
fontColor: "white",
},
}],
yAxes: [{
gridLines: {
display: false,
color: '#03A5C5',
lineWidth: 8,
},
ticks: {
fontColor: "white",
beginAtZero: true,
}
}]
}
};
var ctx = document.getElementById('rpu-chart');
var chart = new Chart(ctx, {
responsive: true,
type: 'line',
data: lineChartData,
options: options
});
If you are using ChartJS 1, then look at the first possible fixes below. If you are using ChartJS 2, then apparently this bug has been fixed (GitHub issue #762). However, after some long debugging I found out that when display: none; is used with maintainAspectRatio: false, some times the height of the graph is squashed to none, which I think it's your problem here. I have logged an issue for this.
Possible fixes (1 is very simple, so you might want to try that):
1. Use jQuery to initially hide the containers
Remove the style="display:none;" from the #rpu div:
<div class="card2 full-chart-topmargin" id='rpu'>
Use jQuery to hide it initially:
$(document).ready(function(){
$("#rpu").hide();
// ...
});
2. Use fixed size canvases
Set both canvas to some fixed size:
<canvas id="revenue-chart" width="600" height="400"></canvas>
<canvas id="rpu-chart" width="600" height="400"></canvas>
Then use maintainAspectRatio: true instead:
var options = {
maintainAspectRatio: true,
// ...
};
In the html, on the element with the id='rpu' try to add "opacity: 0" instead of "display: none", and in the custom.js file instead of show and hide change to:
$(document).ready(function(){
$('#chart-graph-progress').on('change', function() {
if ( this.value == 'revenue-opt')
{
$("#revenue").css("opacity", "1");
}
else
{
$("#revenue").css("opacity", "0");
}
});
$('#chart-graph-progress').on('change', function() {
if ( this.value == 'rpu-opt')
{
$("#rpu").css("opacity", "1");
}
else
{
$("#rpu").css("opacity", "0");
}
});
});
I am pretty sure that the issue is that the chart is not initialized on a display: none element. So we're trying to hide the element by opacity:0.
I hope it helps!
I am trying to implement an Angular horizontal stacked bar chart- Something like this example.
However, I want just one bar that is stacked.
I am working with AngularJS and Chart.js. I have the example showing on the page.
In the PieController, ChartData contains:
{"data":["63","38"],"labels":["Ford","GM"]}
In the example, instead of the label on the outside, I would like the label and then the number to be inside the chart. Like [=====Ford 63====|===GM 38===] the equals represent bar colors. There will be more data points than the current two.
Here is my page
<div ng-controller="PieController">
data {{ChartData}} //testing purposes
<div ng-init="getBarChart()">
<canvas id="Chart1"></canvas>
</div>
Here is my JavaScript controller
app.controller('PieController', function($scope, ChartService) {
$scope.getBarChart = function(){
ChartService.get({name: 'main'}, function(data) {
$scope.ChartData = data;
var barOptions_stacked = {
tooltips: {
enabled: false
},
hover: {
animationDuration: 0
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
scaleLabel: {
display: false
},
gridLines: {},
stacked: true
}],
yAxes: [{
gridLines: {
display: false,
color: "#fff",
zeroLineColor: "#fff",
zeroLineWidth: 0
},
ticks: {
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
stacked: true
}]
},
legend: {
display: false
},
animation: {
onComplete: function () {
var chartInstance = this.chart;
var ctx = chartInstance.ctx;
ctx.textAlign = "left";
ctx.font = "9px Open Sans";
ctx.fillStyle = "#fff";
Chart.helpers.each(this.data.datasets.forEach(function (dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
Chart.helpers.each(meta.data.forEach(function (bar, index) {
data = dataset.data[index];
if (i == 0) {
ctx.fillText(data, 50, bar._model.y + 4);
} else {
ctx.fillText(data, bar._model.x - 25, bar._model.y + 4);
}
}), this)
}), this);
}
},
pointLabelFontFamily: "Quadon Extra Bold",
scaleFontFamily: "Quadon Extra Bold",
};
var ctx = document.getElementById("Chart1");
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: ["2014", "2013", "2012", "2011"],
datasets: [{
data: [727, 589, 537, 543, 574],
backgroundColor: "rgba(63,103,126,1)",
hoverBackgroundColor: "rgba(50,90,100,1)"
}, {
data: [238, 553, 746, 884, 903],
backgroundColor: "rgba(163,103,126,1)",
hoverBackgroundColor: "rgba(140,85,100,1)"
}, {
data: [1238, 553, 746, 884, 903],
backgroundColor: "rgba(63,203,226,1)",
hoverBackgroundColor: "rgba(46,185,235,1)"
}]
},
options: barOptions_stacked,
})
});//end chat service.get
}
});
Instead of hard coding in the dataset which is what its doing now, is there a way I can use the data "ChartData" that I outlined in the beginning of the post?
I'm not sure how to do it or if its even possible.
In order to get the label to show in the bar, reference the labels property of $scope.ChartData:
ctx.fillText($scope.ChartData.labels[index] +" "+ data, 50, bar._model.y + 4);
Instead of hard coding in the dataset which is what its doing now, is there a way I can use the data "ChartData" that I outlined in the beginning of the post?
Use properties from the data variable (supplied by the ChartService get function) i.e. data.labels and data.data instead of the hard-coded values.
So update the labels line from:
labels: ["2014", "2013", "2012", "2011"],
To this:
labels: data.labels,
And similarly for the datasets:
datasets: [{
data: data.data,
So when creating the Chart it should look like this:
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: data.labels,
datasets: [{
data: data.data,
backgroundColor: "rgba(63,103,126,1)",
hoverBackgroundColor: "rgba(50,90,100,1)"
}]
},
options: barOptions_stacked,
});
Expand the code snippet below for a demonstration.
Note: there is currently an issue with the resize-listener, blocked by a CORS issue - I am trying to find a way to disable that.
Update:
Per your comment about stacking the bars (horizontally) - yes that is possible. Just have one element in the datasets array for each item. One simple way to have this is to use Array.map() to create an array similar to the example:
var bgColors = [
"rgba(63,103,126,1)",
"rgba(50,90,100,1)"
];
var hoverBgColors = [
"rgba(50,90,100,1)",
"rgba(140,85,100,1)"
];
var datasets = data.data.map(function(value, index) {
return {
data: [value],
backgroundColor: bgColors[index],
hoverBackgroundColor: hoverBgColors[index]
}
});
Then use variable datasets when creating the Chart object:
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: data.labels,
datasets: datasets
},
options: barOptions_stacked,
});
Also, there is some weird math going on for the location of the second label but the helper function can be updated like below (I tried dividing the x value by 75 but that may need to be adjusted - I am not sure what "appropriate" values are for that ...):
if (i == 0) {
ctx.fillText($scope.ChartData.labels[i] + " " + data, 50, bar._model.y + 4);
} else {
ctx.fillText($scope.ChartData.labels[i] + " " + data, (bar._model.x - 25) / 75, bar._model.y + 4);
}
var app = angular.module('myApp', []);
app.factory('ChartService', function() {
return { //dummy chart service
get: function(obj, callback) {
var data = {
"data": ["63", "38"],
"labels": ["Ford", "GM"]
};
callback(data);
}
};
});
app.controller('PieController', function($scope, ChartService) {
$scope.getBarChart = function() {
ChartService.get({
name: 'main'
}, function(data) {
$scope.ChartData = data;
var barOptions_stacked = {
tooltips: {
enabled: false
},
hover: {
animationDuration: 0
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
scaleLabel: {
display: false
},
gridLines: {},
stacked: true
}],
yAxes: [{
gridLines: {
display: false,
color: "#fff",
zeroLineColor: "#fff",
zeroLineWidth: 0
},
ticks: {
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
stacked: true
}]
},
legend: {
display: false
},
animation: {
onComplete: function() {
var chartInstance = this.chart;
var ctx = chartInstance.ctx;
ctx.textAlign = "left";
ctx.font = "9px Open Sans";
ctx.fillStyle = "#fff";
Chart.helpers.each(this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
Chart.helpers.each(meta.data.forEach(function(bar, index) {
data = dataset.data[index];
if (i == 0) {
ctx.fillText($scope.ChartData.labels[i] + " " + data, 50, bar._model.y + 4);
} else {
ctx.fillText($scope.ChartData.labels[i] + " " + data, (bar._model.x - 25) / 75, bar._model.y + 4);
}
}), this)
}), this);
}
},
pointLabelFontFamily: "Quadon Extra Bold",
scaleFontFamily: "Quadon Extra Bold",
};
var ctx = document.getElementById("Chart1");
var bgColors = [
"rgba(63,103,126,1)",
"rgba(50,90,100,1)"
];
var hoverBgColors = [
"rgba(50,90,100,1)",
"rgba(140,85,100,1)"
];
var datasets = data.data.map(function(value, index) {
return {
data: [value],
backgroundColor: bgColors[index],
hoverBackgroundColor: hoverBgColors[index]
}
});
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
//use empty labels because the labels are on the bars
labels: data.labels.map(function() {
return '';
}),
datasets: datasets
},
options: barOptions_stacked,
})
}); //end chat service.get
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="PieController">
data {{ChartData}} //testing purposes
<div ng-init="getBarChart()">
<canvas id="Chart1"></canvas>
Im using the Canvasjs chart (http://canvasjs.com) I want to after the chart is render color of div border change to red
<div id="chartContainer" style="height:300px; width:90%; font-family:BMitra; text-align:center; border: 1px solid black;"></div>
<script>
function charting() {
var dataPo = [];
$.getJSON("<?=base_url();?>report/messages/create_report2", function(data) {
for( var i = 0; i < data.length; i++) {
dataPo.push({ label: data[i].label, y: data[i].y });
}
var chart = new CanvasJS.Chart("chartContainer", {
//backgroundColor: "#D2FEFF",
backgroundColor: "transparent",
theme: "theme3",
animationEnabled: true,
title:{
text:"my title",
fontFamily: "BYekan"
},
axisY:{
labelFontFamily: "BYekan"
},
axisX:{
labelFontFamily: "BYekan"
},
toolTip:{
fontSize: 19,
fontFamily: "BYekan",
},
data: [{
type: "column",
dataPoints : dataPo,
}]
});
chart.render();
}
</script>
First of all, your code is missing a few closed brackets.
Then, according to this docs, CanvasJS doesn't provide any callback handle for the render() function, but they say since it's very fast you probably won't have any issue in calling a function right after it.
This means you can change the border color with a jQuery call like this
$("yourDivSelector").css("border-color", "red");
and place it right after chart.render();. Your final code should look like this
<script>
function charting() {
var dataPo = [];
$.getJSON("<?=base_url();?>report/messages/create_report2", function(data) {
for( var i = 0; i < data.length; i++) {
dataPo.push({ label: data[i].label, y: data[i].y });
}
var chart = new CanvasJS.Chart("chartContainer", {
//backgroundColor: "#D2FEFF",
backgroundColor: "transparent",
theme: "theme3",
animationEnabled: true,
title:{
text:"my title",
fontFamily: "BYekan"
},
axisY:{
labelFontFamily: "BYekan"
},
axisX:{
labelFontFamily: "BYekan"
},
toolTip:{
fontSize: 19,
fontFamily: "BYekan",
},
data: [{
type: "column",
dataPoints : dataPo,
}]
});
chart.render();
$("yourDivSelector").css("border-color", "red");
});
}
</script>