We use Keen on a site to track view data. This works well but I’m having an issue with how some of the data is presented in the graphs (using v3.0.5 of the JS SDK). On the users dashboard we have a graph showing the last 4 months data (timeframe : this_4_months). I have a query though -
When the user hovers over one of the columns you see detail in a tooltip e.g. "April 1, 2015 12:00:00 AM" - is there any way to format this tooltip into something more user-friendly? e.g. "April 2015"
Keen.ready(function() {
var query = new Keen.Query('count', {
'eventCollection' : 'profile_views',
'timeframe' : 'this_4_months',
'interval' : 'monthly',
'groupBy' : 'view.membership_type',
'filters' : [
{
'property_name' : 'view.user_id',
'operator' : 'eq',
'property_value' : String(user_id)
}
]
});
client.draw(query, document.getElementById(element_id), {
chartType: 'columnchart',
width : graph_width,
height : 250,
colors : ['#abdd99', '#8dc7d9', '#eeeeee'],
colorMapping : {
'pro' : '#abdd99',
'basic' : '#8dc7d9'
},
labelMapping: {
"basic": "BASIC",
"pro": "PRO"
},
title : '',
chartOptions: {
width : '100%',
height : '100%',
isStacked: true,
fontName : 'Helvetica',
fontSize : '11px',
chartArea : {
left : '10px',
top : '0',
width : '90%',
height : '90%'
},
axisTitlesPosition : 'in',
vAxis : {
viewWindowMode : 'pretty',
gridlines : { color : '#eeeeee' },
baselineColor : '#eeeeee',
textPosition : 'in'
},
hAxis : {
viewWindowMode : 'pretty',
gridlines : {
color : '#eeeeee'
},
baselineColor : '#eeeeee',
textPosition : 'none'
},
legend : {
position : 'in'
},
titlePosition : 'none'
}
});
});
Here is a screenshot of how the tooltip appears :
Instead of
var chart = keenIoClient.draw(query, document.getElementById("chart2"), options );
Collect all your data to array manually, so you can add more columns there. Here is example with dates and pageviews:
keenIoClient.run(query, function(err, response){
if (err) {
// there was an error!
}
else {
var arrayData = [];
arrayData.push(['Day','Views']);
response.result.forEach(function (element, index, array) {
var date = new Date(element.timeframe.start);
arrayData.push([date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear() , element.value]);
});
var data = google.visualization.arrayToDataTable(arrayData);
var chart = new google.visualization.ColumnChart( document.getElementById('chart2'));
chart.draw(data, options);
}
});
The "options" variable stays the same.
P.S. on the way you can set up the format for your dates.
P.P.S. in my case the Query was:
var query = new Keen.Query("count", {
eventCollection: "views",
interval: "daily",
timeframe: "this_7_days",
});
Related
In am chart, labels having square bracket in it are ignoring the content between [] brackets on category axis . I checked and ensured that they are passed as string instead of array, even I tried to return this as html ,that also did not worked out for me
Now I am expecting the label to be shown completely irrespective of its content when it is string. Can any one suggest some ways to overcome this .Thanks in advance
My code:
am4core.useTheme(am4themes_animated);
var config = {
yAxes: [
{
id: "c1",
type: "CategoryAxis",
dataFields : {
category : "country"
},
renderer : {
minGridDistance : 20,
minWidth : 120,
grid : [{
location : 0
}]
}
}
],
xAxes: [
{
id: "v1",
type: "ValueAxis",
dataFields : {
value : "visits"
},
renderer : {
maxLabelPosition : 0.98
}
}
],
series: [
{
type: "ColumnSeries",
name: "Series Title",
dataFields: {
valueX: "visits",
categoryY: "country"
},
sequencedInterpolation : true,
sequencedInterpolationDelay : 100,
adapter : {
tooltipText : function(val) {
return 'hello' + val // doesn't work?
}
},
columns : [
{
strokeOpacity : 1, // has no effect?
template : {
adapter : {
"fill" : function (fill, target) {
return target.dataItem.index // not quite right
}
}
}
}
]
}
],
cursor : {
type : "XYCursor",
behavior : "zoomY"
},
colors : {
saturation : 0.4 // does not affect colors?
}
};
var chart = am4core.createFromConfig(config, "chartdiv", am4charts.XYChart);
chart.data = [{
"country": "[UAS]STATES",
"visits": 3025
}, {
"country": "China",
"visits": 1882
},{
"country": "Russia",
"visits": 580
}, {
"country": "South Korea",
"visits": 443
}, {
"country": "Canada",
"visits": 441}]
Brackets in amcharts are treated as reference to data.
Basically, it goes like this: whenever amCharts 4 displays a text, it passes it via text processor we call Text formatter. During the process anything contained within curly brackets { ... } is treated as a reference to some data value and is replaced with relative data. Similarly everything that goes within square brackets [ ... ] is treated as text formatting options, and again is not displayed but rather parsed for text styling instructions.
To solve your issue you need to escape brackets, like so:
country: "[[UAS]]STATES",
Take a look in Docs:
https://www.amcharts.com/docs/v4/concepts/formatters/formatting-strings/#Escaping
I have the following code running:
var options = {
chart: {
type: 'donut',
fontFamily: 'Lato Light'
},
series: [1,2,3,4,5],
labels: ['1','2','3','4','5'],
theme: {
monochrome: {
enabled: true,
color: '#b19254',
shadeTo: 'dark',
shareIntensity: 0.15
}
},
//colors: ['#b19254', '#9f834c', '#8e7543', '#7c663b', '#b99d65', '#c8b387'],
legend: {
position: 'bottom'
},
plotOptions: {
pie: {
donut: {
labels: {
show: true,
name: {
show: false
},
value: {
offsetY: -1,
show: true
},
total: {
show: false,
showAlways: false,
formatter: function (w) { return String(Math.round(chart.w.globals.seriesTotals.reduce((a,b) => { return a+b}, 0) * 100) / 100) + ' ' + $currency}
}
}
}
}
},
}
var chart = new ApexCharts(document.querySelector("#investment-chart-wrapper"), options);
chart.render();
var $chartData = chart.dataURI();
$chartData.then(
(result) => {
document.querySelector('#chartimg').setAttribute('src',result.imgURI);
});
The bit I am fighting with is the promise result of the dataURI() method from here.
For some reason, the chart I get has all the information including the series labels, but the color for the series does not show, leaving me with this. The color is used for the legend at the bottom, however.
I am sure I am missing something here. Please let me know what.
I was running into this problem as well today. It was because the animation of the chart has not taken place yet. You have to get the dataURI() after it has fully rendered or turn off the chart animation.
I was able to get this working by setting the rendered chart to a variable at the top of my js file and then using it in a function like this:
function SetChartImage() {
chartHistoricalPCTArea.dataURI().then(({ imgURI }) => {
var image = document.querySelector('#HistoricalPCTImage');
image.src = imgURI;
})
}
I am trying to plot a simple chart, but want to colour the upper and lower parts of the chart red to indicate a value out of range, and the center part of the chart green to indicate an acceptable value.
Using chartjs-plugin-annotation.js and three Box Annotations, I am almost there. Unfortunately the three boxes don't seem to want to honour the 'yMax' and 'yMin' coordinates I specify. The boxes expand out to the edges in the Y direction. This is what I want them to do in the X direction. But in the Y direction I'm trying to specify the demarcation between the central, green "ok" area and the upper and lower "danger" zones.
If someone can point me in the direction of a solution, I'd very much appreciate it! Here is my code attempt:
<!DOCTYPE HTML>
<html>
<head>
<title>Test Chart</title>
<script src='./moment.js'></script>
<script src='./Chart.js'></script>
<script src='./chartjs-plugin-annotation.js'></script>
</head>
<body>
<canvas id='TestChart' width='1200' height='300'>
<script>
var user_notes = [ '','','','','Note One','','','','',
'Note Two','','','','','','','','','','' ];
new Chart (
document.getElementById( 'TestChart' ), {
'type' : 'line',
'data' : {
'datasets' : [{
'label' : 'Test Chart',
'fill' : false,
'borderColor' : '#000000',
'colour' : '#c0ffa0',
'lineTension' : 0.4,
'pointHitRadius' : 10,
'pointRotation' : 45,
'pointRadius' : [ 5,5,5,5,9,5,5,5,5,9,5,5,5,5,5,5,5,5,5,5 ],
// 'pointRadius' : (item, data) => { // function better but
// console.log( 'TEST pointRadius' );// can't get it to work
// return 7;
// },
'pointStyle' : [ 'circle','circle','circle','circle','rect',
'circle','circle','circle','circle','rect',
'circle','circle','circle','circle','circle',
'circle','circle','circle','circle','circle' ],
// 'pointStyle' : (item, data) => { // function better but
// console.log( 'TEST pointStyle' ); // can't get it to work
// return 'triangle';
// },
'pointBackgroundColor' : [ '#ffa0c0', '#ffa0c0', '#ffa0c0',
'#c0ffa0', '#c0ffa0', '#c0ffa0',
'#c0ffa0', '#ffa0c0', '#c0ffa0',
'#c0ffa0', '#c0ffa0', '#c0ffa0',
'#c0ffa0', '#c0ffa0', '#c0ffa0',
'#c0ffa0', '#ffa0c0', '#c0ffa0',
'#c0ffa0', '#c0ffa0' ],
// a function call would be better
// but can't get either of the following to work
// // function type one
// 'pointBackgroundColor' : (item, data) => {
// console.log( 'pointBackgroundColor TEST' );
// return '#c0ffa0';
// },
// // function type two
// 'pointBackgroundColor' : function( context ) {
// var v = context.dataset.data[c_index];
// console.log( 'pointBackgroundColor TEST: v = ' + v );
// return
// (v <= 3.9) || (v >= 6)
// ? '#ffa0c0' // draw extreme values in red
// : '#c0ffa0'; // else, values in green
// },
'data' : [
{ x: new Date( '2019-11-03 07:19' ), y: 3.9 },
{ x: new Date( '2019-11-04 07:31' ), y: 3.8 },
{ x: new Date( '2019-11-05 08:15' ), y: 3.6 },
{ x: new Date( '2019-11-06 08:20' ), y: 4.2 },
{ x: new Date( '2019-11-07 08:08' ), y: 4.2 },
{ x: new Date( '2019-11-08 08:00' ), y: 4.2 },
{ x: new Date( '2019-11-09 08:18' ), y: 4.4 },
{ x: new Date( '2019-11-10 07:59' ), y: 3.5 },
{ x: new Date( '2019-11-11 08:02' ), y: 4.8 },
{ x: new Date( '2019-11-12 08:23' ), y: 4.4 },
{ x: new Date( '2019-11-13 07:54' ), y: 4.0 },
{ x: new Date( '2019-11-14 08:00' ), y: 4.3 },
{ x: new Date( '2019-11-15 08:11' ), y: 4.2 },
{ x: new Date( '2019-11-16 08:11' ), y: 4.9 },
{ x: new Date( '2019-11-17 08:10' ), y: 4.9 },
{ x: new Date( '2019-11-18 08:15' ), y: 5.8 },
{ x: new Date( '2019-11-19 07:10' ), y: 6.1 },
{ x: new Date( '2019-11-20 08:03' ), y: 5.1 },
{ x: new Date( '2019-11-21 07:51' ), y: 4.7 },
{ x: new Date( '2019-11-22 07:56' ), y: 4.3 }
]
}]
},
options: {
'point' : {
'borderColor' : '#000000',
},
'tooltips' : {
'mode' : 'index',
'bodyFontSize' : 24,
'footerFontSize' : 18,
'footerFontColor' : '#ffeb6d',
'axix' : 'y',
'callbacks' : {
'title' : (item, data) => {
var when = item[0].xLabel.split( ' ' );
var newT = when[0] + ' ' + when[1] + ' ' + when[3];
return newT.substring(0, newT.length - 3);
},
'label' : (item, data) => {
return item.yLabel.toFixed( 1 ) + ' Units';
},
'footer' : (item, data) => {
return user_notes[ item[0].index ];
},
},
}, // end tooltips
'legend' : {
'onClick' : 'null',
// 'display' : 'false',
'position' : 'top',
},
'scales' : {
'xAxes' : [ {
'type' : 'time',
'ticks' : {
'time' : { 'displayFormats' : { 'day' : 'D MMM' } },
}
} ],
'yAxes' : [ {
'display' : 'true',
'position' : 'right',
'ticks' : {
'suggestedMax' : 9.0,
'suggestedMin' : 3.0,
}
} ],
}, // end scales
'annotation' : {
'drawTime' : 'beforeDatasetsDraw',
'annotations' : [{
'type' : 'box',
'id' : 'top-red',
'xScaleID' : 'xAxes',
'yScaleID' : 'yAxes',
'yMin' : '5.9', //ignored
'borderWidth' : 0,
'borderColor' : 'rgba(255,200,200,0.3)',
'backgroundColor' : 'rgba(255,200,200,0.3)',
},{
'type' : 'box',
'id' : 'middle-green',
'xScaleID' : 'xAxes',
'yScaleID' : 'yAxes',
'yMax' : '5.9', // ignored
'yMin' : '4.0', // ignored
'borderWidth' : 0,
'borderColor' : 'rgba(200,255,200,0.3)',
'backgroundColor' : 'rgba(200,255,200,0.3)',
},{
'type' : 'box',
'id' : 'bottom-red',
'xScaleID' : 'xAxes',
'yScaleID' : 'yAxes',
'yMax' : '4.0', // ignored
'borderWidth' : 0,
'borderColor' : 'rgba(255,200,200,0.3)',
'backgroundColor' : 'rgba(255,200,200,0.3)',
}]
} // end annotation
} // end options
}
);
</script>
</body>
</html>
This behavior comes from the fact that you did not provide the correct y-axis id and x-axis id in the boxes definition.
I see that you did not put any id for x-axis and y-axis in your chart's xAxes and yAxes definition, so their default ids are 'x-axis-0' and 'y-axis-0'.
For each of your boxes definition, try changing
'xScaleID' : 'xAxes',
'yScaleID' : 'yAxes'
into
'xScaleID' : 'x-axis-0',
'yScaleID' : 'y-axis-0'
I need help to put the number of the pie chart in the legend
Chart Image
If i hover the chart with mouse i can see the number relative to each item
i want to display it in the legend either
the important code so far:
var tempData = {
labels: Status,
datasets: [
{
label: "Status",
data: Qtd,
backgroundColor: randColor
},
]
};
var ctx = $("#pieStatus").get(0).getContext("2d");
var chartInstance = new Chart(ctx, {
type: 'pie',
data: tempData,
options: {
title: {
display: true,
fontsize: 14,
text: 'Total de Pedidos por Situação'
},
legend: {
display: true,
position: 'bottom',
},
responsive: false
}
});
"Qtd","randColor" are "var" already with values
You need to edit the generateLabels property in your options :
options: {
legend: {
labels: {
generateLabels: function(chart) {
// Here
}
}
}
}
Since it is quite a mess to create on your own a great template. I suggest using the same function as in the source code and then edit what is needed.
Here are a small jsFiddle, where you can see how it works (edited lines - from 38 - are commented), and its result :
Maybe this is a hacky solution, but for me seems simpler.
The filter parameter
ChartJS legend options have a filter parameter. This is a function that is called for each legend item, and that returns true/false whether you want to show this item in the legend or not.
filter has 2 arguments:
legendItem : The legend item to show/omit. Its properties are described here
data : The data object passed to the chart.
The hack
Since JS passes objects by reference, and filter is called for each legend item, then you can mutate the legendItem object to show the text that you want.
legend : {
labels: {
filter: (legendItem, data) => {
// First, retrieve the data corresponding to that label
const label = legendItem.text
const labelIndex = _.findIndex(data.labels, (labelName) => labelName === label) // I'm using lodash here
const qtd = data.datasets[0].data[labelIndex]
// Second, mutate the legendItem to include the new text
legendItem.text = `${legendItem.text} : ${qtd}`
// Third, the filter method expects a bool, so return true to show the modified legendItem in the legend
return true
}
}
}
Following on from tektiv's answer, I've modified it for ES6 which my linter requires;
options: {
legend: {
labels: {
generateLabels: (chart) => {
const { data } = chart;
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
const meta = chart.getDatasetMeta(0);
const ds = data.datasets[0];
const arc = meta.data[i];
const custom = (arc && arc.custom) || {};
const { getValueAtIndexOrDefault } = Chart.helpers;
const arcOpts = chart.options.elements.arc;
const fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
const stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
const bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
const value = chart.config.data.datasets[arc._datasetIndex].data[arc._index];
return {
text: `${label}: ${value}`,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: Number.isNaN(ds.data[i]) || meta.data[i].hidden,
index: i,
};
});
}
return [];
},
},
},
},
I wanted to let the user select from 100+ data sets, but rather than adding/removing them from my Chart I decided to set the showLine: false on any dataset that I want hidden. Unfortunately the default legend would show all 100+. So in my solution I generate the legend manually, filtering out any dataset that has showLine: false.
Your settings will have this:
legend: {
labels: {
generateLabels: (a) => {
return a.data.labels
}
}
And you'll generate your own labels with a helper function:
function updateAllLabels() {
const myNewLabels = [];
myChart.data.datasets.forEach((element) => {
if (element.showLine) {
myNewLabels.push(generateLabel(element));
}
});
myChart.data.labels = myNewLabels;
}
And you'll generate the label with another function:
function generateLabel(data) {
return {
fillStyle: data.borderColor,
lineWidth: 1,
strokeStyle: data.borderColor,
text: data.countyName, // I attach countryName to my datasets for convenience
}
}
Now just don't forget to call the function whenever updating your chart:
updateAllLabels();
myChart.update();
Happy graphing!
I am having a CFGRID that contains multiple columns, the grid exceed the page size due to the amount of columns.
I been looking up on how to add a horizontal scroll bar to the grid so the grid can be within the page but scroll through the columns, but I am unable to find a working example or a answer on how to do this.
I have solved the problem.
It turns out that you are need to specify both the Height and Width column, I only specified the Width column.
Thanks,
If you define a width to your grid, it should scroll to view the other columns. This this example:
http://jsfiddle.net/YRraU/2/
var
grid = Ext.create('Ext.grid.Panel', {
store: store,
stateful: true,
stateId: 'stateGrid',
columns: [
{
text : 'Company',
flex : 1,
sortable : false,
dataIndex: 'company',
width : 100
},
{
text : 'Price',
width : 75,
sortable : true,
renderer : 'usMoney',
dataIndex: 'price'
},
{
text : 'Change',
width : 75,
sortable : true,
renderer : change,
dataIndex: 'change'
},
{
text : '% Change',
width : 75,
sortable : true,
renderer : pctChange,
dataIndex: 'pctChange'
},
{
text : 'Last Updated',
width : 85,
sortable : true,
renderer : Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'
},
{
xtype: 'actioncolumn',
width: 50,
items: [{
icon : '../shared/icons/fam/delete.gif', // Use a URL in the icon config
tooltip: 'Sell stock',
handler: function(grid, rowIndex, colIndex) {
var rec = store.getAt(rowIndex);
alert("Sell " + rec.get('company'));
}
}, {
getClass: function(v, meta, rec) { // Or return a class from a function
if (rec.get('change') < 0) {
this.items[1].tooltip = 'Hold stock';
return 'alert-col';
} else {
this.items[1].tooltip = 'Buy stock';
return 'buy-col';
}
},
handler: function(grid, rowIndex, colIndex) {
var rec = store.getAt(rowIndex);
alert((rec.get('change') < 0 ? "Hold " : "Buy ") + rec.get('company'));
}
}]
}
],
height: 350,
width: 300, // 30 pixel width defined here
title: 'Array Grid',
renderTo: 'grid',
viewConfig: {
stripeRows: true
},
listeners: {
viewready: function(){
var c = this.columns[5];
var p = c.getPosition();
this.scrollByDeltaX(p[0]);
}
}
});
});
You can also search on extjs grid horizontal scrol, rather than CFGRID to find more examples.
It turns out that you are need to specify both the Height and Width column, I only specified the Width column.