Chart.js - access yAxes properties - arrays

From everything I've seen we access the y axis on a 2-axis cartesian graph like so:
chart.options.scales.yAxes[0]
However I get 'undefined'. This despite the fact that my chart is generated with the following dictionary:
{
type: 'line',
data: {
...
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
tooltips: {
enabled: false,
}
}
}
This suggests the same method of access as I attempted. However chart.options.scales.yAxes returns object Object, so that is defined. Why can I not access it as an Array, which other examples I've seen, and the method of defining the chart, clearly show it to be?

Your options are defined in the Chart.js version 2 syntax.
scales.[x/y] axes arrays were removed in Chart.js version 3 (see specific changes). Scales are now configured directly to options.scales object with the object key being the scale id.
options: {
scales: {
y: {
beginAtZero: false
}
}
}
It seems that your chart.options.scales are overwritten with default values by Chart.js because they don't comply with the expected format. This would explain why chart.options.scales.yAxes returns an Object instead of an Array.

Related

Label in BarChart?

Is it anyhow possible to set the label into the bar chart?
Like this example:
I couldnt find an example on the official page: Official example page
//EDIT
Later I had other problems, but I was able to solve them by switching to Nivo Charts. Nivo Charts
You could do this using label options in this way:
options: {
scales: {
xAxes: [
{
ticks: {
autoSkip: false,
maxRotation: 90,
minRotation: 90,
padding: -110
}
}
]
}
}
The result is:
This is a codesandbox example.
You can use the LabelList component with a custom render, here is the link of the documentation:
https://recharts.org/en-US/api/LabelList
also you can use the position property to center it inside the bar , here is an example of a customized LabelList :
https://recharts.org/en-US/examples/BarChartWithMinHeight

react-chart-js-2 units on y axis

I'm using react-chart-js-2 and I want to add units to the y-axis. There doesn't seem to be much documentation about the full range of options and the tutorials I found don't seem to be working. I want to add a £ sign to every value on the y axis of my line chart? I should just be able to use a callback function and add £ to the value string?
const options = {
scales: {
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Years'
}
}],
yAxes: [{
ticks: {
beginAtZero: true,
callback: value => `£${formatNumberDecimal(value)}`
}
}],
}
}
This doesn't work, and when I dynamically change the input data, it crashes. How do I change the units?
I hope it's not too late!
I don't know much about chart.js api, but i think you can't use ES6 arrow functions inside that callback, try with something like:
callback: function (value) {
return `£${value}k`;
},
Here is a working codesandbox example.

Kendo grid export to excel header title shows html characters

When I export my grid to excel,headers are like: Product Name,{{'unitsOrder'| translate}} in excel. My grid supports 2 languages and I am showing it with angularjs translate way. Any offer?
<script>
$("#grid").kendoGrid({
toolbar: ["excel"],
excel: {
fileName: "Kendo UI Grid Export.xlsx"
},
dataSource: {
type: "odata",
transport: {
read: "http://demos.telerik.com/kendo-ui/service/Northwind.svc/Products"
},
pageSize: 7
},
sortable: true,
pageable: true,
columns: [
{ width: 300, field: "ProductName", title: "<b>Product Name</b>" },
{ field: "UnitsOnOrder", title: "{{'unitsOrder'| translate}}" },
{ field: "UnitsInStock", title: "Units In Stock" }
]
});
</script>
I had a similar problem but I use i18next translation instead of angular but maybe it helps you find a solution to your case:
I use the 'excelExport' event to manually update the generated worksheet. Within the worksheet object I search for the header cells and manually trigger the translation for the text it contains:
excelExport: function(e) {
// First I loop through all rows in the worksheet
e.workbook.sheets[0].rows.forEach(function(row){
// Ignore 'data' rows (only use 'header' and 'footer')
if(row.type != 'data'){
// Loop through all cells of the row
row.cells.forEach(function(cell){
// Here I overwrite the cell value with its translation
// You have to implement translate() so it works with angular
cell.value = translate(cell.value)
});
}
});
},
You now have to write your own translate function which can handle your angular translation. As I use i18next for translations my solution won't help here (I use jquery to generate a jquery html object on which I can trigger the translation)

Backbone fetching a model: Parsing format in array of objects

My model in the server-side has an attribute which is an array of objects. In the client-side I'm trying to fetch that model in a backbone's model.
After fetching the model from my server I get this object:
{
round: 17,
username: bob,
football_bets: [
0: { one: true, x: false, two: false },
1: { one: false, x: false, two: true },
2: { one: true, x: true, two: false },
]
}
However, I was expecting something like this:
{
round: 17,
username: bob,
football_bets: [
{ one: true, x: false, two: false },
{ one: false, x: false, two: true },
{ one: true, x: true, two: false },
]
}
I understand that this is something related with how backbone parses the result of a fetch action, but I don't know how I should deal with.
Should I override the parse method of the model to get the result as I expect?
Should I use collections instead of trying to model everything inside of a single model?
Should I use some third-party library to deal with nested objects?
I really appreciate any kind of suggestions!
The format google-chrome console adopts for arrays is like the first array I've posted in the question. I was thinking that the numbers appears before the object itself was something that backbone it was adding when you perform a fetch action ... I was thinking this because when I debugged my code I had an error that said me it was not possible to access to an attribute.
After more debugging I realize that there was an indexation problem trying to access to an undefined element ... So the conclusion of all this mess is how the google-chrome console formats arrays! Hope this helps somebody ...

Legend Template - Chart

I got this template (default)
<span class="x-legend-item-marker {[values.disabled?'x-legend-inactive':'']}" style="background:{mark};"></span>{name}
that produce this :
I want to have the same template with every of it's functionnality. But, I need one more if-clause to it. I don't want an item to be 'legendarize' if it's value is 0.
Here is the complete code
{
xtype: 'container',
title: 'Chart',
iconCls: 'chart',
itemId: 'chart_Tab',
layout: {
type: 'fit'
},
items: [
{
xtype: 'polar',
itemId: 'pie',
colors: [
'#115fa6',
'#94ae0a',
'#a61120',
'#ff8809',
'#ffd13e',
'#a61187',
'#24ad9a',
'#7c7474',
'#a66111',
'#222222',
'#115ea6',
'#94cc0a',
'#b61120',
'#dd8809',
'#11d13e',
'#a68887',
'#94df9d',
'#7f74f4',
'#112341',
'#abcdef1'
],
store: 'relativedata',
series: [
{
type: 'pie',
label: {
textBaseline: 'middle',
textAlign: 'center',
font: '9px Helvetica'
},
labelField: 'strName',
labelOverflowPadding: 0,
xField: 'numValue'
}
],
interactions: [
{
type: 'rotate'
}
],
listeners: [
{
fn: function(element, eOpts) {
var relStore = Ext.getStore('relativedata');
var eleStore = Ext.getStore('element');
var relModel;
var eleModel;
relStore.removeAll();
//Convert to CO2 qty
for(var i = 0; i< eleStore.getCount();i++)
{
eleModel = eleStore.getAt(i);
relModel = Ext.create(APPNAME + '.model.RelativeElement');
relModel.set('strName',eleModel.get('strName'));
relModel.set('numValue', eleModel.get('numValue')*eleModel.getFactor());
relStore.add(relModel);
}
relStore.sync();
//Hide arrows-legend
this._series[0]._label.attr.hidden=true;
},
event: 'painted'
}
],
legend: {
xtype: 'legend',
docked: 'bottom',
itemId: 'pie_legend',
itemTpl: [
'<span class="x-legend-item-marker {[values.disabled?\'x-legend-inactive\':\'\']}" style="background:{mark};"></span>{name}'
],
maxItemCache: 100,
store: 'element'
}
}
]
}
I ask for help because i'm not that good with templates. I would not dare say I understand everything of the default one actually.
I'm back! Yet, nobody's calling me slim shaddy for that... Unluckily!
So, to answer your initial question, the template you need would be something like the following:
// Configuration of the chart legend
legend: {
// Finally, we can use the value field to customize our templates.
itemTpl: [
'<tpl if="value != 0">', // <= template condition
'<span class="x-legend-item-marker {[values.disabled?\'x-legend-inactive\':\'\']}" style="background:{mark};"></span>{name}',
'</tpl>'
]
// ...
}
Unfortunately, as I've said in my previous comment, quick debugger inspection shows that this value variable, or any equivalence, is not available at the time this template is applied.
Now I'm going to give you a detailed explanation about how I was able to overcome this vexation. In part because this is such an involved hack that you'd better know what you're doing if you decide to apply it, and in part because you'll learn a lot more by witnessing the fishing techniques than by being given the fish right away -- in this case, the fish is not available for retail anyway. And also in a large part, I must confess, because I like to be lyrical about things I've put some energy in, and it's late, and my defenses against self congratulation have gotten a bit weak...
So, looking at Ext.chart.Legend's code shows that there's nothing to be done there, it's just a somewhat lightweight extension of Ext.dataview.Dataview. As such it must have a store bounded to it, which, obviously (and unfortunately), is not the one bound to the chart to provide its data.
Another judicious breakpoint (in the Legend's setStore method) shows that this store comes from Ext.chart.AbstractChart, and in the code of this class we can see two things: a dedicated legend store is created in the constructor, and chart series implement a method to feed this store, namely provideLegendInfo.
We're getting closer to our goal. What we need to do is add a value field to the legend store, and have our serie provide the data for this field. Great!
The wise approach now would be to implement these modifications with the minimal amount of replication of Ext's code... But after having spent an inconsiderate amount of time trying to do that with no luck, I'll just settle for wildly overriding these two methods, and giving the advice to put a big bold warning to check that the code of these methods doesn't change with the next versions of Touch:
if (Ext.getVersion().isGreaterThan('2.2.1')) {
// Give yourself a big warning to check that the overridden methods' code
// bellow has not changed (see further comments).
}
With that out of the way, let's go to the point without any further consideration for future generations.
That is, first we add a value field to the legend store:
/**
* Adds a value field to legend store.
*/
Ext.define(null, {
override: 'Ext.chart.AbstractChart'
// Berk, what a lot of code replication :( Let's just hope that this method's code
// won't change in the future...
,constructor: function() {
var me = this;
me.itemListeners = {};
me.surfaceMap = {};
me.legendStore = new Ext.data.Store({
storeId: this.getId() + '-legendStore',
autoDestroy: true,
fields: [
'id', 'name', 'mark', 'disabled', 'series', 'index'
// Adding my value field
,'value'
]
});
me.suspendLayout();
// For whatever reason, AbstractChart doesn't want to call its superclass
// (Ext.draw.Component) constructor and, by using callSuper, skips directly to
// Ext.Container's one. So well... I respect, but I must do it old school since
// callSuper would go to Ext.draw.Component from here.
Ext.Container.prototype.constructor.apply(this, arguments);
// me.callSuper(arguments);
me.refreshLegendStore();
me.getLegendStore().on('updaterecord', 'onUpdateLegendStore', me);
me.resumeLayout();
}
}, function() {
// Post-create functions are not called for overrides in touch as they are
// in ExtJS? Hmm... That would have been the perfect place to issue a big
// warning in case the version has changed, but we'll live with it :(
});
And, second, we make our chart serie feed that value. From your code, I can deduce that you're working with a pie chart, so I'm only giving the code for that, as a matter of illustration... But, if you've followed until here, it should be trivial to implement it for other kind of series. Anyway, here's the code:
/**
* Overrides `provideLegendInfo` to add the value to the legend records.
*
* Here again, let us all cross our fingers very hard, hoping for Sencha's team to not decide
* to add their own extra fields too soon...
*/
Ext.define(null, {
override: 'Ext.chart.series.Pie'
,provideLegendInfo: function(target) {
var store = this.getStore();
if (store) {
var items = store.getData().items,
labelField = this.getLabelField(),
field = this.getField(),
hidden = this.getHidden();
for (var i = 0; i < items.length; i++) {
target.push({
name: labelField ? String(items[i].get(labelField)) : field + " " + i,
mark: this.getStyleByIndex(i).fillStyle || this.getStyleByIndex(i).strokeStyle || 'black',
disabled: hidden[i],
series: this.getId(),
index: i
// Providing actual data value to the legend record
,value: items[i].get(field)
});
}
}
}
});
Let's sum it up. We've got two overrides and a custom template. We could hope that we'd be done by now. But here's what we get:
So, the DataView is adding some markup of its own around the itemTpl's markup. Well, well, well... At this point, I'm tired of tracking Ext's internals and, fortunately (for once!), I envision a quick patch for this. So that is without an hesitation that I'm throwing this CSS rule in:
.x-legend-item:empty {
display: none;
}
And finally we're done. I guess my line of thought and code might be a little tricky to replicate, so let me provide you with a definitive proof that this all works.
In this demo, there is a "metric four" that has a value of 0.
{
'name': 'metric four',
'data': 0
}
But you won't see it. Because that was the point of all this, wasn't it?

Resources