I have the following AngularJS App:
angular.module("app-machines", ['ngFlatDatepicker'])
.factory('MachinesService', ['$http', MachinesService])
.controller('mainController', ['$scope', 'MachinesService', '$timeout', mainController])
.directive('onFinishRender', function ($timeout)
{
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
});
}
}
}
});
Where here is what happens (pasting code would be too long).
User has two controls (date from and date to) on a page, which upon changing fire an event which downloads data from a website (json).
Afterwards I am simply storing the returned json into an object $scope.machines.
Then I wait for ng-repeat on my webpage to finish rendering components (in my case for every item under ng-repeat I am creating a canvas where chart would be stored like following)
<div class="col-md-12" ng-repeat="machine in machines" on-finish-render="ngRepeatFinished">
<h1> {{ machine.name }}</h1>
<canvas id="{{'myChart_' + $index}}" width="400" height="400"></canvas>
</div>
Once this has finished rendering, I am calling the following function
$scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
//you also get the actual event object
//do stuff, execute functions -- whatever...
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First dataset",
// The properties below allow an array to be specified to change the value of the item at the given index
// String or array - the bar color
backgroundColor: "rgba(100,220,220,0.2)",
// String or array - bar stroke color
borderColor: "rgba(220,220,220,1)",
// Number or array - bar border width
borderWidth: 1,
// String or array - fill color when hovered
hoverBackgroundColor: "rgba(220,220,220,0.2)",
// String or array - border color when hovered
hoverBorderColor: "rgba(220,220,220,1)",
// The actual data
data: [65, 59, 80, 81, 56, 55, 40],
// String - If specified, binds the dataset to a certain y-axis. If not specified, the first y-axis is used.
yAxisID: "y-axis-0",
},
{
label: "My Second dataset",
backgroundColor: "rgba(220,220,220,0.2)",
borderColor: "rgba(220,220,220,1)",
borderWidth: 1,
hoverBackgroundColor: "rgba(220,220,220,0.2)",
hoverBorderColor: "rgba(220,220,220,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
var options = {
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true
}]
}
};
for (var i = $scope.machines.length - 1; i >=0; i--) {
var ctx = $("#myChart_" + i);
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
myBarChart.update();
console.log("processed " + i);
}
});
Here is the problem that occurs. The page is generated, I can see all my canvas with the charts, however the bars are only displayed for one chart only. The bars on the remaining charts have for unknown reason hidden bar. However I can still hover over them like this:
FINAL
So in the end the problem was caused by the fact, that all my charts were bound to the same data source (I had a static data source for now). Once I changed it to dynamic data source (all charts had their own dataset) it suddenly worked like a charm.
What am I doing wrong here?
Related
I have a page full of charts that automatically generates all charts available (because the default page is "All Charts"). In it, there's a select department tag that will hide all charts other than those owned by the selected department. Here's my code:
$(window).load(function(){
$('#department').change(function(){
active_department($(this).val());
});
function active_department(department){
for(var i = 0; i < dept['namedept'].length; i++){
if(department!='All'){
$('.'+dept['namedept'][i]).hide(500);
} else {
if(typeof rCharts[dept['namedept'][i]] != 'undefined'){
$('.'+dept['namedept'][i]).show(500);
} else {
$('.no-chart-'+dept['namedept'][i]).hide(500);
}
}
}
if(typeof rCharts[department] != 'undefined'){
$('.'+department).show(500);
} else {
$('.no-chart-'+department).hide(500);
}
}
});
I want ChartJS animation to re-appear every time I select a department. So far I've tried easing, onProgress, and jQuery animate. none's working. Is it possible to re-animate the chart? If so, how?
From this answer and from the lack of options available in the Docs, it looks like the only feasible options would be these hacks:
redraw the chart with JS using new Chart or
change some minor configuration, or recreate an instance of the chart data and then call the update() method.
e.g.: Call the data through a function, and when you want the animation to happen, call the same function again. Because it now has a new array (even though it's the same data), the chart re-animates.
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<button onclick="updateChart()">Update</button>
<canvas id="myChart"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var chartData = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: createDataset()
}
};
var chart = new Chart(ctx, chartData);
function updateChart(){
chartData.data.datasets = createDataset()
chart.update();
}
function createDataset(){
return [{
label: "My First dataset",
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: [0, 10, 5, 2, 20, 30, 45],
fill: false
}];
}
//ignore next line, it's to deal with a bug from chartjs cdn on stackoverflow
console.clear();
</script>
Right now I am implementing line chart in angular js. And I have written one directive for this, So it is working fine, But when I am putted $watch in this directive then every time chart will updating continuously. I want to use $watch for some dynamic change after loaded the page.
angular.module('app.abc').directive('linechart', function () {
return {
restrict: 'A',
template:'',
link: function (scope, element, attributes) {
scope.$watch(function(){
var lineOptions = {
scaleShowGridLines : true,
scaleGridLineColor : "rgba(0,0,0,.05)",
scaleGridLineWidth : 1,
bezierCurve : true,
};
var lineData = { labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
var ctx = element[0].getContext("2d");
var myNewChart = new Chart(ctx).Line(lineData, lineOptions);
})
}
}
});
<canvas style="display: block;" linechart height="120" ></canvas>
Line chart reflecting updates is contiguously. But I don't want to reflection continuously. I have tried to put my ctx object outside of $watch, but chart is not showing anything. Actually I am new in angular js and also new in directives. Please give me idea how handle this directive with $watch. I have seen one demo(based on javascript) example https://codepen.io/SitePoint/pen/mJRrKw
I've been working with ChartJS for the last couple of weeks and I'm getting used to it, however, I'm trying to add individual labels to my bars in my barchart and I can't figure it out.
Below is the code I'm using.
var config = {
type : 'bar',
data : {
datasets : [ {
label: numberOfFailures, //This line is the problem
data : failureData,
backgroundColor : colours,
} ],
labels : labels
},
options : {
responsive : true,
legend : {
position : 'bottom'
}
}
};
If I change the word label to labels they don't show up at all, but when it says label they all show up together. What I want is for array element 1 to appear on bar 1, etc.
If your aim is to take the values from an array and have them appear along the bottom of the bar chart so that each array value is the label for a bar then you need to set the data.labels value, not the data.datasets.label value.
For example, this basic chart is taken from the Chart.js documentation for bar chart data structure and shows how to use an array of month names to label the bars. Notice that the label bars go into the data.labels node.
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First dataset",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: [65, 59, 80, 81, 56, 55, 40],
}
]
};
If you programmatically create an array with one label for each point of data then it might look something like this:
var chartConfig = {};
for (score = 0; score < maxScore; ++score) {
chartConfig.scoreLabels[score] = score;
chartConfig.scoreData[score] = howManyAchievedScore(score);
}
var data = {
labels: chartConfig.scoreLabels,
datasets: [
{
label: "Number of players who achieved score",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: chartConfig.scoreData,
}
]
};
You don't have to create the labels and data values inside a single object, but it's usually tidier if you can group your chart configuration data into one object so that you can pass it from one function to another with one parameter.
The data.datasets.label value does something different, providing text which appears in the chart legend and in tooltips which appear when you hover over a bar.
I seem to be getting errors in Chart.JS for some reason. It's related to the global options that you can set.
The is as follows
Here is my code
var chart1 = document.getElementById("chart1").getContext("2d"),
chart2 = document.getElementById("chart2").getContext("2d"),
chart3 = document.getElementById("chart3").getContext("2d"),
datatest1 = document.getElementById("datatest1").value,
datatest2 = document.getElementById("datatest2").value,
color_bg = "#00b5e4",
color_fg = "#007799",
data1 = [{ value: Math.floor(Math.random() * 100), color: color_bg}, { value: Math.floor(Math.random() * 100), color: color_fg}],
data2 = [{ value: datatest1, color: color_bg}, { value: datatest2, color: color_fg}],
data3 = {
labels: ["Jan", "Feb", "Mar"],
datasets: [
{
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
},
{
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,0.8)",
highlightFill: "rgba(151,187,205,0.75)",
highlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
//
// #Global Chart Settings
var options = Chart.defaults.global = {
animation: true,
animationSteps: 160,
animationEasing: "easeOutQuart",
responsive: true,
showTooltips: true,
segmentShowStroke: false,
maintainAspectRatio: true,
percentageInnerCutout: 70,
onAnimationComplete: function () {
"use strict";
//console.log("Animation Done");
}
};
$(document).ready(function () {
"use strict";
//
// #Initialise and bind to data and global options
new Chart(chart1).Doughnut(data1, options);
new Chart(chart2).Doughnut(data2, options);
new Chart(chart3).Radar(data3);
});
If you remove the options from the charts they work, if you add options and set them globally as per their documentation you get the error I've mentioned. Am I missing something obvious or is there an issue here?
When you do
var options = Chart.defaults.global = {
...
you are setting the COMPLETE Chart global default to your object. Unless you have ALL the Chart global options in your object, this will cause many of the options to end up as undefined. The right way to set the global options is like so
Chart.defaults.global.animation = true;
Chart.defaults.global.animationSteps = 160;
...
i.e. change the value of the individual properties in global instead of setting the entire global property.
Global options for charts can be set like this:
Chart.defaults.global = {
//Set options
So you can just delete the
var = options
This sets default values for all of your future charts
Please refer to the documentation for further instructions: http://www.chartjs.org/docs/
If you want to specify options for each charts do this:
new Chart(ctx).Line(data, {
// options here
});
First it must be understood that the main property to be tweaked so as to handle the animations is "Chart.defaults.global.animation".
Then keeping this as the base, you will need to adjust the sub-properties as mentioned above and in the documentation page.
So, You can change as follows:
Chart.defaults.global.animation.animationSteps=160;
Chart.defaults.global.animation.duration=5000;
...
For positioning of these lines of code, follow the first answer by potatopeelings.
I have tried changing in this way and it works !!!
I'm using chart.js plugin and using a group chart by bar view.
when i hover a group of bars i can see a tooltip that show me the data of this bars.
but i what to change the tooltip to show my only single data when I'll hover the bar data.
and I what to show diffrent data info.
jsfiddle example
var ctx = document.getElementById("errorChart").getContext("2d");
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 0, 0, 0, 0, 0, 0]
},
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,0.8)",
highlightFill: "rgba(151,187,205,0.75)",
highlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
var myBarChart = new Chart(ctx).Bar(data);
You could extend the bar graph to include this functionality. By default it will return both bars at the index you have hovered over, it will also check for multiple bars at the area you hovered before creating the tooltip and put any extras in that were missing.
So to do this you will need to override two functions getBarsAtEvent and showToolTip here is an example and fiddle
I have tried to make it clear the two important areas that have changed look at the comments in the extended bar type. Small changes were also made to any reference of the helpers as before they were within the scope but now they need to explicitly call Chart.helpers
Chart.types.Bar.extend({
name: "BarOneTip",
initialize: function(data){
Chart.types.Bar.prototype.initialize.apply(this, arguments);
},
getBarsAtEvent : function(e){
var barsArray = [],
eventPosition = Chart.helpers.getRelativePosition(e),
datasetIterator = function(dataset){
barsArray.push(dataset.bars[barIndex]);
},
barIndex;
for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
//change here to only return the intrested bar not the group
barsArray.push(this.datasets[datasetIndex].bars[barIndex]);
return barsArray;
}
}
}
return barsArray;
},
showTooltip : function(ChartElements, forceRedraw){
console.log(ChartElements);
// Only redraw the chart if we've actually changed what we're hovering on.
if (typeof this.activeElements === 'undefined') this.activeElements = [];
var isChanged = (function(Elements){
var changed = false;
if (Elements.length !== this.activeElements.length){
changed = true;
return changed;
}
Chart.helpers.each(Elements, function(element, index){
if (element !== this.activeElements[index]){
changed = true;
}
}, this);
return changed;
}).call(this, ChartElements);
if (!isChanged && !forceRedraw){
return;
}
else{
this.activeElements = ChartElements;
}
this.draw();
console.log(this)
if (ChartElements.length > 0){
//removed the check for multiple bars at the index now just want one
Chart.helpers.each(ChartElements, function(Element) {
var tooltipPosition = Element.tooltipPosition();
new Chart.Tooltip({
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
xPadding: this.options.tooltipXPadding,
yPadding: this.options.tooltipYPadding,
fillColor: this.options.tooltipFillColor,
textColor: this.options.tooltipFontColor,
fontFamily: this.options.tooltipFontFamily,
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
caretHeight: this.options.tooltipCaretSize,
cornerRadius: this.options.tooltipCornerRadius,
text: Chart.helpers.template(this.options.tooltipTemplate, Element),
chart: this.chart
}).draw();
}, this);
}
return this;
}
});
then to use it just do what you did before but use BarOneTip (call it whatever you like, what ever is in the name attribute of the extended chart will be available to you.
var ctx = document.getElementById("errorChart").getContext("2d");
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 0, 0, 0, 0, 0, 0]
},
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,0.8)",
highlightFill: "rgba(151,187,205,0.75)",
highlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
var myBarChart = new Chart(ctx).BarOneTip(data);
I should mention that if chartjs gets updated you would need to manually put any changes to the functions into the overridden ones