I am new to charts/angular, and it's been a long time since I used javascript. I'm trying to use a factory in Angular to grab data from a server and use it to populate a chart (which uses Angular Charts, a wrapper around Charts.js). I can populate it originally with fake data, but changing it at all causes the chart to go blank. Do I have some sort of scoping issue here? Here's the relevant code:
.controller("LineCtrl", ["$scope", "HRDataPointFactory", function ($scope, HRDataPointFactory) {
//The following lines cause the chart to populate, but obviously I dont care about this fake data.
//$scope.labels = ["January", "February", "March", "April", "May", "June", "July"];
//$scope.series = ["Series A", "Series B"];
//$scope.data = [
// [65, 59, 80, 81, 56, 55, 40],
// [28, 48, 40, 19, 86, 27, 90]
//];
$scope.onClick = function(points, evt) {
console.log(points, evt);
};
$scope.data = [[]];
$scope.labels = [];
$scope.series = ["Department"];
$scope.getData = function() {
HRDataPointFactory.getData($scope.department, $scope.dt1, $scope.dt2)
.then(function (data) {
var i;
for (i = 0; i < data.data.length; i++) {
$scope.data[0].push(data.data[i].EmployeeCount);
}
for (i = 0; i < data.data.length; i++) {
$scope.labels.push(data.data[i].Date);
}
});
}
/*unrelated stuff here*/
}
])
.factory("HRDataPointFactory", function HRDataPointFactory($http) {
var exports = {};
exports.getData = function(deptKey, start, end) {
var config = {
params: { departmentKey: deptKey, startTime: start, endTime: end }
}
return $http.get("/HR/Data", config)
.then(function(response) {
console.log(response);
return response;
},function(data) {
console.log("There was an error!", data);
return response;
});
};
return exports;
});
And the html:
<canvas id="line" class="chart chart-line" chart-data="data"
chart-labels="labels" chart-legend="true" chart-series="series"
chart-click="onClick">
</canvas>
Thank you for the help!
I created a Plunker with a working example. To simplify I only pull the data (not the labels, etc.), but that is easy to complete.
In the controller, we assign vm to this and in the function getData I reference vm.data. Before you had $scope which is a different $scope in the function that the one you assign as a blank array.
.controller('MyCtrl', ['$scope', 'HRDataPointFactory', function($scope, HRDataPointFactory) {
var vm = this;
//The following lines cause the chart to populate, but obviously I dont care about this fake data.
vm.labels = ["January", "February", "March", "April", "May", "June", "July"];
vm.series = ["Series A", "Series B"];
// vm.data = [
// [65, 59, 80, 81, 56, 55, 40],
// [28, 48, 40, 19, 86, 27, 90]
// ];
vm.onClick = function(points, evt) {
console.log(points, evt);
};
vm.data = [
[]
];
vm.getData = function() {
HRDataPointFactory.getData($scope.department, $scope.dt1, $scope.dt2)
.then(function(success) {
console.log(JSON.stringify(success));
vm.data = success.data;
});
}
vm.getData();
/*unrelated stuff here*/
}])
I print in the console the data I receive:
{"data":[[65,59,80,81,56,55,40],[28,48,40,19,86,27,90]],"status":200,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"params":{},"url":"data","headers":{"Accept":"application/json, text/plain, */*"}},"statusText":""}
The data is hosted on plunker too using this as a format:
[
[65, 59, 80, 81, 56, 55, 40],
[28, 48, 40, 19, 86, 27, 90]
]
Please not that I also used the "Controller as" syntax in the HTML to respect best practices... see john papa's article on this subject.
<div ng-controller="MyCtrl as vm">
<canvas id="line" class="chart chart-line" chart-data="vm.data"
chart-labels="vm.labels" chart-legend="true" chart-series="vm.series"
chart-click="onClick">
</canvas>
</div>
Let us know.
Related
I need to display every month User Revenue details in chart. Here I'm using PHP CodeIgniter frame work. In below I have mentioned my user controller:
public function Revenue_byuser() {
$rev_data = [];
$rev_array = [];
$getuser_info = $this->User_Model->getuser_alldetail();
for($i=0;$i<=count($getuser_info);$i++) {
$get_rev = $this->User_Model->getrev_byuserid($getuser_info[$i]->id);
foreach($get_rev as $key => $val) {
$rev_data[] = $val['total_revenue']??'0';
}
$rev_array[] = array('name' => $getuser_info[$i]->id, 'data' => $rev_data);
}
$Return['revenue_data'] = $rev_array;
$this->resp_output($Return);
exit;
}
My User Model:
public function getuser_alldetail() {
return $this->db->get_where('login', ['dept' => 1, 'status' => 1])->result() ;
}
public function getrev_byuserid($userid) {
$query = "select sum(revenue) as total_revenue,DATE_FORMAT(upd_date,'%Y-%m') AS revmonth from revenue_details WHERE added_by = ? AND DATE_FORMAT(upd_date, '%y') = DATE_FORMAT(CURRENT_DATE, '%y') GROUP BY revmonth ORDER BY revmonth ASC";
return $this->db->query($query,[$bdid])->result_array();
}
And This chart.js file:
$(window).on("load", function(){
var siteurl = window.location.origin;
$.ajax({
url: siteurl+'/User/Revenue_byuser',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(resp) {
var revuser_option = {
chart: {
height: 250,
type: 'line',
toolbar:{
show: false
}
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
series: resp.revenue_data,
xaxis: {
type: 'date',
categories: ["Jan", "Feb", "Mar", "April", "May", "June", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
},
tooltip: {
x: {
format: 'dd/MM/yy'
},
},
colors: ['#111211','#15ad15','#0cebdc','#f21335','#f2b313','#131ff2','#cd13f2','#111411','#16ad15','#2cebdc','#f28335','#f9b313']
};
var user_revenue = new ApexCharts(document.querySelector("#rev_byuser"),revuser_option);
user_revenue.render();
}
});
});
Then when print my response I'm getting same revenue details for all users.
But I want to print like this:
series: [{
name: 'Username-1',
data: [31, 40, 28, 51, 42, 109, 100, 17, 87, 12, 7, 89]
}, {
name: 'Username-2',
data: [33, 32, 76, 12, 44, 62, 31, 54,96,8,5,54]
},
{
name: 'Username-3',
data: [21, 50, 38, 41, 52, 10, 20]
}, {
name: 'Username-4',
data: [11, 32, 45, 32, 34, 52, 41]
}],
Where I made mistake here please help me out.
If I understand you correctly, you are experiencing an ever increasing array for rev_data. On the second line of the public function Revenue_byuser(), you specify $rev_data as such:
$rev_data = [];
A bit further on you fill that array with a new index:
foreach($get_rev as $key => $val) {
$rev_data[] = $val['total_revenue']??'0';
}
Problem is, you don't empty the array after you've add the data for that specific user. As such, you keep pushing data into an array which you re-use for each user.
To solve this, empty the array after you've added the data for a user:
$rev_array[] = array('name' => $getuser_info[$i]->id, 'data' => $rev_data);
$rev_data = [];
Consider the following code.
var chartData = {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [{
fillColor: "#79D1CF",
strokeColor: "#79D1CF",
data: [60, 80, 81, 56, 55, 40]
}]
};
var ctx = document.getElementById("myChart1").getContext("2d");
var myLine = new Chart(ctx, {
type: "line",
data: chartData,
showTooltips: false,
onAnimationComplete: function() {
var ctx = this.chart.ctx;
ctx.font = this.scale.font;
ctx.fillStyle = this.scale.textColor
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
this.datasets.forEach(function(dataset) {
dataset.points.forEach(function(points) {
ctx.fillText(points.value, points.x, points.y - 10);
});
})
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<canvas id="myChart1" height="300" width="500"></canvas>
Because I use the function in the onAnimationComplete in different graphs, I like to create a JavaScript file where I collect all the functions and use them. For example
var chart = new Chart(ctx, eval(config));
chart.options.animation.onComplete = function () {
window["AnimationComplete1"](chart);
}
function AnimationComplete1(chart) {
var ctx = chart.ctx;
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
chart.data.datasets.forEach(function (dataset) {
dataset.points.forEach(function (points) {
ctx.fillText(points.value, points.x, points.y - 10);
});
})
}
In this code, the problem is that in the function I don't have access to the point from the dataset from chart.data.datasets. Also, I don't have access to the scale for font and textColor.
Is there a way to access those values from a common function?
The Chart.js animation onComplete callback function must be defined inside the chart options.
options: {
animation: {
onComplete: ctx => {
// do your stuff
}
}
}
Please take a look at your amended code below and see how it works.
var chartData = {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [{
fillColor: "#79D1CF",
strokeColor: "#79D1CF",
data: [60, 80, 81, 56, 55, 40]
}]
};
new Chart('myChart1', {
type: "line",
data: chartData,
options: {
animation: {
onComplete: ctx => {
console.log(ctx.chart.data.datasets);
console.log(ctx.chart.options.scales.x);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.min.js"></script>
<canvas id="myChart1" height="300" width="500"></canvas>
I cannot debug the following code. I would like to update chart data (not add on top; delete current data and add completely new dataset). (Not)Working example on codepen:
https://codepen.io/anon/pen/bvBxpr
var config = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
data: [65, 0, 80, 81, 56, 85, 40],
fill: false
}]
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, config);
labelsNew = ["Why", "u", "no", "work", "???"];
dataNew = [2, 4, 5, 6, 10];
function updateData(chart, label, data) {
removeData();
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
};
function removeData(chart) {
chart.data.labels.pop();
chart.data.datasets.forEach((dataset) => {
dataset.data.pop();
});
chart.update();
}
$('.button-container').on('click', 'button', updateData(myChart, labelsNew, dataNew));
I figured it out. This works:
function addData(chart, label, data) {
chart.data.labels = label
chart.data.datasets.forEach((dataset) => {
dataset.data = data;
});
chart.update();
}
$("#btn").click(function() {
addData (myChart, labelsNew, dataNew);
});
instead of pushing the data (which adds on), data needs to be allocated by " = ".
I see 2 problems:
in function updateData() missing chart argument to removeData(chart);
click handler for button, use simply:
$("#btn").click(function() {
updateData(myChart, labelsNew, dataNew)
});
var config = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
data: [65, 0, 80, 81, 56, 85, 40],
fill: false
}]
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, config);
labelsNew = ["Why", "u", "no", "work", "???"];
dataNew = [2, 4, 5, 6, 10];
function addData(chart, label, data) {
chart.data.labels = label
chart.data.datasets.forEach((dataset) => {
dataset.data = data;
});
chart.update();
}
function clickupdate(){
addData(myChart, labelsNew, dataNew);
}
.chart-container {
height: 300px;
width: 500px;
position: relative;
}
canvas {
position: absolute;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="button-container">
<button onclick="clickupdate()">Change Data</button>
</div>
<div class="chart-container">
<canvas id="myChart"></canvas>
</div>
I have an angularjs line chart in my page. I am using chart.js as my chart. The data that I have at first is fake and I want to get the true data from the database. I have created a query that can get the date and the total number of members on that specific date. The query goes like this:
SELECT distinct a.time, COUNT(b.member_id) AS Members
FROM b
Inner JOIN a ON b.mir_id = a.mir_id
GROUP BY
a.time order by a.time
And this query returns data like this
I have a line chart code in my controller that goes like this one:
$scope.member;
adminService.getMember()
.then(function(data){
$scope.memberList = data.data.member;
$scope.member = $scope.memberList;
console.log($scope.memberList);
//alert($scope.ptypeList);
//localStorage.ProductType = JSON.stringify(data.data);
$rootScope.loader = false;
});
$scope.percent = 65;
$scope.anotherPercent = -45;
$scope.anotherOptions = {
animate:{
duration:0,
enabled:false
},
barColor:'#2C3E50',
scaleColor:false,
lineWidth:20,
lineCap:'circle'
};
$scope.percent2 = 25;
$scope.anotherPercent2 = -45;
$scope.anotherOptions2 = {
animate:{
duration:0,
enabled:false
},
barColor:'#2C3E50',
scaleColor:false,
lineWidth:20,
lineCap:'circle'
};
$scope.percent3 = 85;
$scope.anotherPercent3 = -45;
$scope.anotherOptions3 = {
animate:{
duration:0,
enabled:false
},
barColor:'#2C3E50',
scaleColor:false,
lineWidth:20,
lineCap:'circle'
};
//--------------
$scope.colors = ['#45b7cd', '#ff6384', '#ff8e72'];
$scope.labels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
$scope.data = [
[65, -59, 80, 81, -56, 55, -40],
[28, 48, -40, 19, 86, 27, 90]
];
$scope.datasetOverride = [
{
label: "Bar chart",
borderWidth: 1,
type: 'bar'
},
{
label: "Line chart",
borderWidth: 3,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
type: 'line'
}
];
$scope.labels1 = ['2006', '2007', '2008', '2009', '2010', '2011', '2012'];
$scope.series1 = ['Series A', 'Series B'];
$scope.onClick = function (points, evt) {
console.log(points, evt);
};
$scope.datasetOverride1 = [{ yAxisID: 'y-axis-1' }];
$scope.options1 = {
scales: {
yAxes: [
{
id: 'y-axis-1',
type: 'linear',
display: true,
position: 'left',
data: $scope.member
},
]
}
}
I am quite confused on how can I assign the values I get from the database to the line chart. How can I do that?
First you should aggregate your data returned from the database.Use the SUM() function.The correct format should be this:
Date | Member
-------------------
2016-06-23 | 5
2016-06-24 | 10
In the controller you should have an array.
data = [{Date:"2016-0-23",Member:5},{Date:"2016-06-24",Member:10}];
To create a simple chart you need the data and labels.
vm.labels = data.map(function (property) {return propery.Date;});
vm.data = data.map(function (property) {return propery.Member;});
On the HTML page:
<canvas id="line" class="chart chart-line" chart-data="vm.data" chart-labels="vm.labels"></canvas>
I have a Meteor Collection which I want to be presented into a graph using ChartJS. I was able to follow how ChartJS documentation.
My problem now is how to transform my collection and pass it on to ChartJS.
ChartJs Data format :
function drawChart() {
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
data: [28, 48, 40, 19, 86, 27, 90]
}]
};
This is how my collection was saved :
Categories.insert({
categoryname : $('#categoryname').val(),
value : $('#categoryvalue').val()
});
I wanted to use the categoryname as the chart labels and the value as the data. How am I going to do this?
This is how I made it work after another try after I posted my question.
function drawChart() {
var cur = Categories.find();
collData = [];
cur.forEach(function(cat){
collData.push([cat.value]);
});
collLabel = [];
cur.forEach(function(cat){
collLabel.push([cat.categoryname]);
});
var data = {
labels: collLabel,
datasets: [{
data: collData
}]
};
};
I am not sure if this the right way to do it but it works for now.