ChartJS v3.X - Limit the string size of label on canvas, without changing tooltip hover string - chart.js

So, I had some old charts code using a very old Chart.js version 2.x but now due to the need of some functions only found on versions 3.x I'm updating a lot of code from the old charts on the website because a lot of the syntax changed between versions.
I just finished updating all charts and they are working nicely using plugins and etc, except for one.
On this type: 'bar' chart I have some very long strings on the 'X' axis, so I need to trim them to a max of 12 characters, but the tooltips need to show the whole string. The old code used to be this one and worked like a charm, right before rendering the chart I would set the updateScaleDefaults:
// Limit the size of label for 12 characters, tooltip on hover not changed
Chart.scaleService.updateScaleDefaults('category', {
ticks: {
callback: function(tick) {
var characterLimit = 12;
if ( tick.length >= characterLimit) {
return tick.slice(0, tick.length).substring(0, characterLimit -1).trim() + '...';;
}
return tick;
}
}
});
So right after that I would call the new Chart instance and would render it on a canvas. But now this code doesn't work anymore on v3.x, and I can't make an alternative version of this work.
As the migration guide for V3.x states on the documentation ,
Chart.scaleService was replaced with Chart.registry. Scale defaults are now in Chart.defaults.scales[type]
So I tried changing the first line of the code above to this and many other variations, trying to reuse the old code just changing the call to the object:
Chart.defaults.scales['category']
Nothing I tried worked.
I then tried creating a plugin with an beforeDraw, acessing the chart.scales.x.ticks and trying to make an arrow function on a map call, but I got a $context is a object error like this:
const pluginLimitTitle = {
beforeDraw: (chart) => {
console.log(chart.scales.x.ticks);
chart.scales.x.ticks = chart.scales.x.ticks.map(function (tick) {
var characterLimit = 12;
if (tick['label'].length >= characterLimit) {
return tick['label'].slice(0, tick['label'].length).substring(0, characterLimit - 1).trim() + '...';
;
}
return tick;
});
}
I also tried putting the ticks callback inside the options on the chart creation on options: scales: x: ticks but it did not work either way.
Can someone help me make this on v3.x?
Spent the whole day trying many things and don't look I'm getting closer to make it work.

After wasting many hours I found a "Tip" highlight on the documentation that should be in the examples, and not just badly highlighted on the "Labeling Axes" page.
When you do a callback for ticks on the chart options settings, you get 3 params to call:
function(value, index, ticks)
I tried in many ways to change the last one because it is the array of all ticks with their labels, and it is the only one where the label string appears so I was trying to modify it.
You'd think at first that the "value" parameter is the one to be changed, but it returns the exactly same integer value as the "index" parameter on each callback iteration, so I thought the only one the I could manipulate to change the string was the "ticks" array, but I was completely wrong.
You actually need to call a special function called getLabelForValue().
The working code ended up like this:
const configTotal = {
type: 'bar',
data: dataTotal,
options: {
scales: {
y: {
beginAtZero: true
},
x: {
ticks: {
callback: function(value, index, ticks_array) {
let characterLimit = 12;
let label = this.getLabelForValue(value);
if ( label.length >= characterLimit) {
return label.slice(0, label.length).substring(0, characterLimit -1).trim() + '...';
}
return label;
}
}
}
}
},
};
I hope this helps anyone having the same problem as me.
Maybe all the time I wasted is all on me for not reading every single line with more patience, but in my opinion, this documentation lacks a lot more example codes for accessing properties and callback parameters and it's object values, the way it is just showing the type of object and returns of each class method call, makes it very confusing for non-experienced on chart.js users.

Related

SwiftUI make LazyGrid Item twice the size of other Items

I want to display a grid where one item takes twice the amount of space of the others.
Similar to this HTML Grid
Basically I want the last item Text("div4") to take up the space of two items. I do not want to use .fixed since it should take exactly twice the space of the other grids no matter the screen size.
If there is a way to archive this without LazyGrid, that is also fine with me ;)
Here is my current code:
let cols = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
var body : some View {
LazyVGrid(columns: cols){
Text("div1")
Text("div2")
Text("div3")
Text("div4")
}
}
Since Swift does not support this. A solution would be to use a library like: ExyteGrid!
var body : some View {
Grid(tracks: [.fr(1),.fr(1),.fr(1),.fr(2)]){
Text("div1")
Text("div2")
Text("div3")
Text("div4")
}
}
https://cocoapods.org/pods/ExyteGrid

Google Sheets - If statement to populate cell

It feels like this should be really easy, but I keep getting errors related to circular logic.
Column C "Total" will always be entered by the user first. If user enters number in Column B "Variable" then Column A "Fixed" will be populated with C - B. If user enters number in Column A "Fixed", then Column B "Variable" will be populated with C - A.
https://docs.google.com/spreadsheets/d/1xBbU6A_MDK6fyLjdFUD7X06b7BQ1VhQ-FWQBET4cLso/edit?usp=sharing
You are trying to add formulas that will always need to rely on eachother to produce an output and as result of that, it will run into a Circualr Dependency error.
Possible solution:
Try using the "Iterative Calculation" option under File –> Spreadsheet Settings –> Calculation. You can see the description for Iterative Calculation here.
Here is one way to avoid circular references: do not hand enter any formulas, but use an onEdit() script to insert formulas programmatically only when necessary.
The following script will enter a formula in column B when column A is edited, and vice versa:
function onEdit(e) {
if (!e) {
throw new Error('Please do not run the script in the script editor window. It runs automatically when you hand edit the spreadsheet.');
}
const watchSheet = /^(Sheet1|Sheet2|Sheet3|Sheet4)$/i;
const watchColumns = [
{
colNumber: 1,
formula: '=C:C - B:B',
},
{
colNumber: 2,
formula: '=C:C - A:A',
},
];
const sheet = e.range.getSheet();
if (!sheet.getName().match(watchSheet)) {
return;
}
const editedColumn = watchColumns.filter(column => column.colNumber === e.range.columnStart)[0];
if (!editedColumn) {
return;
}
const updateColumns = watchColumns.filter(column => column.colNumber !== editedColumn.colNumber);
updateColumns.forEach(column => {
sheet
.getRange(e.range.rowStart, column.colNumber)
.setFormula(column.formula);
});
}

Using a batchsize greater than 1 when using Tensorflows c++ API

I have a Tensorflow model trained in Python and frozen with the freeze_graph script. I've succesfully loaded the model in c++ and made inference for a single image. However, it seems freeze_graph sets the batchsize to only a single image at a time, since I am unable to pass the model a tensor with more than one image.
Does anyone know a way of changing this? I haven't been able to locate where in the script it actually happens.
Thanks!
EDIT:
Okay, so I scrapped Keras just to eliminate any black magic that might be doing, and I set a batch size of 16 when defining the network with Tensorflow.
If I print the graph def, the placeholder has a shape:
node {
name: "inputs"
op: "Placeholder"
attr {
key: "dtype"
value {
type: DT_FLOAT
}
}
attr {
key: "shape"
value {
shape {
dim {
size: 16
}
dim {
size: 50
}
dim {
size: 50
}
dim {
size: 3
}
}
}
}
}
However, when I attempt to load and run the model in c++ with a tensor of shape 16 x 50 x 50 x 3, I get this error:
tensorflow/core/framework/tensor.cc:433] Check failed: 1 == NumElements() (1 vs. 16)Must have a one element tensor
Something must be happening somewhere when I freeze the graph?
This turned out to be a stupid mistake on my part. When getting the output of the graph, I called .scalar<float>() on it. This worked perfectly fine when I only had one input image, and therefore only one output, but obviously I can't cast a vector to a scalar. Changing it to .flat<float>() fixed my issue.

Nest elements has different padding value

I spent a couple hours to figure it out but no luck, so basically I've this configuration
$susy: (
columns: 12,
gutters: 54px/53px,
container: 1230px
);
In my layout file _layout.scss, I've this
#include layout( $susy inside );
.content-area {
padding-top: gutter( 6 );
.layout-2c-l & {
#include span( 8 of 12 no-gutters );
#include gutters(5); // 62.0625px
}
.archive.layout-2c-l &,
.search.layout-2c-l & {
padding-left: 0;
padding-right: 0;
}
}
In my case, on archive and search page I've to remove the gutters then re-added it via it's child elements like so in _archives.scss file
.page-header {
#include gutters(5); // 41.375px
}
As you can see the code above beside the gutters I add the pixel value, the first gutters resulting 62.0625px and the the second one 41.375px.
If this is how susy works, is there any way to get the same result?
Unless you set math: static in your Susy config, Susy will output % values based on the context that you give it. If you tell it that you are in a context of 5 both places, you will get the same % output — but that will translate into different px values if the parent containers are not actually the same width. So gutter(5) should only be used in places where the parent element spans 5 columns.
It's hard to tell from your sparse code sample, but it looks like you are maybe using the gutter context argument differently from how it is intended. When used correctly, you will always get a gutter ~54px (since that's what your settings specify).

Aggregated Computed property not getting updated in ember

Aggregated Computed property not getting updated in ember
Val.set('arr',[]);
Val.set('arr',[{val:1},],[{val:2}],[{val:3}],[{val:4}]);
Val.reopen({
total:function(){
var array=this.get('arr');
var total=0;
for(i=0;i<array.length;i++)
{
total=total+array[i].val
}
return total;
}.property('arr.#each'),
});
this thing works for the first time but property observers never get called second time whenever array elements are updated.
If you want to observe the properties from the objects in the array then you are missing here something:
...
}.property('arr.#each')
try to change your code to this
...
}.property('arr.#each.val')
otherwise ember will only notice changes on the array itself (removing/adding elements) and not changes to the properties of those objects inside the array.
not part of the answer, but as a inspiration ,you could do your total method more conveniently like so, using the built-in reduce method:
Val.reopen({
total:function(){
var total = this.get('arr').reduce(function(a, b) {
return a.val + b.val;
});
return total;
}
});
hope it helps