Adding string pluralization to amCharts tooltip - amcharts4

I'm using an amCharts v4 Pie chart and currently I have this tooltip that appears when hovering on the slices:
series.slices.template.tooltipText = "{category}: {value.percent.formatNumber('#.')}% ({value} hours)
However, I would like to have the "hours" pluralized correctly, i.e. when {value} equals 1, I would like to have the text hour instead of hours.
Is something like this possible?
I've found the Adapters, but I don't think they will be usable here because I'm using a special format with category, percentage and the text.

Adapters are the perfect use case for this. You can look at the dataItem associated with the tooltip and use that logic to determine whether return the pluralized version of your tooltip or use the default one, for example:
pieSeries.slices.template.adapter.add('tooltipText', function(tooltipText, target) {
if (target.dataItem && target.dataItem.value == 1) {
return tooltipText.replace('hours', 'hour')
}
else {
return tooltipText;
}
});
Demo below:
var chart = am4core.create("chartdiv", am4charts.PieChart);
chart.data = [{
"country": "Lithuania",
"hours": 1
}, {
"country": "Czechia",
"hours": 2
}, {
"country": "Ireland",
"hours": 3
}, {
"country": "Germany",
"hours": 1
}, {
"country": "Australia",
"hours": 1
}, {
"country": "Austria",
"hours": 1
}];
var pieSeries = chart.series.push(new am4charts.PieSeries());
pieSeries.dataFields.value = "hours";
pieSeries.dataFields.category = "country";
pieSeries.slices.template.tooltipText = "{category}: {value.percent.numberFormatter('#.')}% ({value} hours)";
pieSeries.slices.template.adapter.add('tooltipText', function(tooltipText, target) {
if (target.dataItem && target.dataItem.value == 1) {
return tooltipText.replace('hours', 'hour')
}
else {
return tooltipText;
}
});
chart.legend = new am4charts.Legend();
#chartdiv {
width: 100%;
height: 400px;
}
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<div id="chartdiv"></div>

Related

Hide grids in Bar Graph

import React from 'react';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
);
const options = {
responsive: true,
elements: {
bar: {
borderWidth: 2,
},
},
plugins: {
scales: {
x: {
grid: {
display: false
}
},
y: {
grid: {
display: false
}
},
},
legend: {
display: false,
},
title: {
display: false,
// text: 'Top Application Accessed',
},
},
};
const labels = ["a", "b", "c", "d0", "d", "e", "t"];
const v = [8, 10, 15, 2, 4, 11, 17]
const data = {
labels,
datasets: [
{
label: "Total no of errors",
data: v,
backgroundColor: 'blue',
}
],
};
export default function App() {
return <Bar options={options} data={data} />;
}
In the above code All the code inside scales is not having any effect.
I want to hide grids from my chart.
I want to also add some features to my chart but anything I add to this code having no effect in the results.
Instead of full grids I want dotted grid only parallel to x axis.
I also want to add different colors to all the bars.
You have putted your scale condif in the plugins section of the options object. This is incorrect, you need to place the scales section in the root of the options object. Then it will work fine.
So instead of options.plugins.scales.x and options.plugins.scales.y you will get: options.scales.x and options.scales.y

Category labels having brackets([]) are not showing complete label

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

Conditional in ChartJS axes tick callback function isn't returning the expected labels

I have a chart containing data for each day of the year and I'm wanting to show the x-axis simply as months.
I've set up the following callback function which (crudely) grabs the month from the set of labels, checks to see whether it already exists and if not, returns it as an axis label
let rollingLabel;
...
function(label, index, labels) {
let _label = label.replace(/[0-9]/g, '');
if (rollingLabel != _label) {
rollingLabel = _label;
return rollingLabel;
}
}
However, it's only returning two of the expected four labels.
What's confusing me more is that if I add console.log(rollingLabel) within the conditional I can see that the variable is updating how I'd expect but it's not returning the value, or it is and the chart isn't picking it up for whatever reason. Even more confusing is that if I uncomment line 48 // return _label the chart updates with all the labels so I don't believe it's an issue with max/min settings for the chart.
If anyone has any ideas I'd be most grateful. I've been staring at it for hours now!
The expected output for the below snippet should have the following x-axis labels:
Aug | Sep | Oct | Nov
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
let data = [
1,6,3,11,5,1,2,6,2,10,5,8,1,1,2,4,5,2,3,1
];
let labels = [
"Aug 1","Aug 2","Aug 3","Aug 4","Aug 5","Sep 1","Sep 2","Sep 3","Sep 4","Sep 5","Oct 1","Oct 2","Oct 3","Oct 4","Oct 5","Nov 1","Nov 2", "Nov 3","Nov 4","Nov 5"
];
let rollingLabel;
chart = new Chart(ctx, {
type: "line",
data: {
datasets: [
{
backgroundColor: '#12263A',
data: data,
pointRadius: 0
}
],
labels: labels,
},
options: {
legend: {
display: false
},
responsive: false,
scales: {
xAxes: [
{
gridLines: {
display: false
},
ticks: {
display: true,
autoSkip: true,
callback: function(label, index, labels) {
let _label = label.replace(/[0-9]/g, '');
if (rollingLabel != _label) {
rollingLabel = _label;
return rollingLabel;
}
// return _label;
}
}
}
]
},
tooltips: {
mode: "index",
intersect: false
},
hover: {
mode: "index",
intersect: false
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart"></canvas>
You need to define ticks.autoSkip: false on the x-axis to make it work as expected:
autoSkip: If true, automatically calculates how many labels can be shown and hides labels accordingly. Labels will be rotated up to maxRotation before skipping any. Turn autoSkip off to show all labels no matter what.
Please take a look at your amended code below:
let data = [
1,6,3,11,5,1,2,6,2,10,5,8,1,1,2,4,5,2,3,1
];
let labels = [
"Aug 1","Aug 2","Aug 3","Aug 4","Aug 5","Sep 1","Sep 2","Sep 3","Sep 4","Sep 5","Oct 1","Oct 2","Oct 3","Oct 4","Oct 5","Nov 1","Nov 2", "Nov 3","Nov 4","Nov 5"
];
let rollingLabel;
chart = new Chart('chart', {
type: "line",
data: {
datasets: [
{
backgroundColor: '#12263A',
data: data,
pointRadius: 0
}
],
labels: labels,
},
options: {
legend: {
display: false
},
responsive: false,
scales: {
xAxes: [
{
gridLines: {
display: false
},
ticks: {
display: true,
autoSkip: false,
callback: function(label, index, labels) {
let _label = label.replace(/[0-9]/g, '');
if (rollingLabel != _label) {
rollingLabel = _label;
return rollingLabel;
}
}
}
}
]
},
tooltips: {
mode: "index",
intersect: false
},
hover: {
mode: "index",
intersect: false
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart"></canvas>
I found a easy solution via the chart.js documentation.
const config = {
type: 'line',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Chart with Tick Configuration'
}
},
scales: {
x: {
ticks: {
// For a category axis, the val is the index so the lookup via getLabelForValue is needed
callback: function(val, index) {
// Hide the label of every 2nd dataset
return index % 2 === 0 ? this.getLabelForValue(val) : '';
},
color: 'red',
}
}
}
},
};
The callback function decides what labels will be shown. Current setup shows every 2nd label, if you want to show every 3rd for example you would change:
return index % 2 === 0 ? this.getLabelForValue(val) : '';
to:
return index % 3 === 0 ? this.getLabelForValue(val) : '';

AmCharts 4 - column's bullet/babel position

everyone
So, I'm having some trouble with AmCharts 4 again.
Is there any way to always show the column's bullet/label?
My case
We can use this example, from AmCharts documentation, to reproduce my situation. Just set labelBullet.label.dy to 20 positive.
labelBullet.label.dy = 20;
https://codepen.io/team/amcharts/pen/VxbVeq
Thanks.
You can set maskBullets to false on the chart instance to prevent it from clipping the LabelBullet, e.g. chart.maskBullets = false;
Demo:
// Create chart instance
var chart = am4core.create("chartdiv", am4charts.XYChart);
// Add data
chart.data = [{
"date": new Date(2018, 3, 20),
"value": 90
}, {
"date": new Date(2018, 3, 21),
"value": 102
}, {
"date": new Date(2018, 3, 22),
"value": 65
}, {
"date": new Date(2018, 3, 23),
"value": 62
}, {
"date": new Date(2018, 3, 24),
"value": 55
}, {
"date": new Date(2018, 3, 25),
"value": 81
}];
chart.maskBullets = false;
// Create axes
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
// Create value axis
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
// Create series
var lineSeries = chart.series.push(new am4charts.LineSeries());
lineSeries.dataFields.valueY = "value";
lineSeries.dataFields.dateX = "date";
lineSeries.name = "Sales";
lineSeries.strokeWidth = 3;
// Add simple bullet
var circleBullet = lineSeries.bullets.push(new am4charts.CircleBullet());
circleBullet.circle.stroke = am4core.color("#fff");
circleBullet.circle.strokeWidth = 2;
var labelBullet = lineSeries.bullets.push(new am4charts.LabelBullet());
labelBullet.label.text = "{value}";
labelBullet.label.dy = 20;
<script src="//www.amcharts.com/lib/4/core.js"></script>
<script src="//www.amcharts.com/lib/4/charts.js"></script>
<div id="chartdiv" style="width: 100%; height: 300px;"></div>

How to programmatically collapse raddataform "Groups" in Nativescript-vue

I'm trying to make use of the RadDataForm using NativeScript-Vue to develop a relatively long form.
My form elements are being defined and grouped programmatically via json. I would like to have the groups start in a minimized / collapsed state for the user experience.
The NativeScript-Vue docs for this feature are very sparse. Angular documentation is deeper so I went there for help, but clicking thru to the "collapsed" API reference results in a 404 from this page.
I'm running it from the Playground to test functionality using the following code:
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar">
<ActionItem icon="font://" class="fa" />
<ActionItem icon="font://\uf07a" class="fa" />
</ActionBar>
<RadDataForm :source="person" :metadata="groupMetaData"
#groupUpdate="onGroupUpdate" />
</Page>
</template>
<script>
import Vue from "nativescript-vue";
import RadDataForm from "nativescript-ui-dataform/vue";
Vue.use(RadDataForm);
export default {
data() {
return {
person: {
name: "John",
age: 23,
email: "john#company.com",
city: "New York",
street: "5th Avenue",
streetNumber: 11
},
groupMetaData: {
// propertyGroup: [{
// "Address": {
// 'collapsed' = true,
// },
// // {
// // "Main Info": 'collapsed',
// }
// ],
propertyAnnotations: [{
name: "city",
index: 3,
groupName: "Address",
editor: "Picker",
valuesProvider: [
"New York",
"Washington",
"Los Angeles"
]
},
{
name: "street",
index: 4,
groupName: "Address"
},
{
name: "streetNumber",
index: 5,
editor: "Number",
groupName: "Address"
},
{
name: "age",
index: 1,
editor: "Number",
groupName: "Main Info"
},
{
name: "email",
index: 2,
editor: "Email",
groupName: "Main Info"
},
{
name: "name",
index: 0,
groupName: "Main Info"
}
]
}
};
},
methods: {
onGroupUpdate: function(args) {
let nativeGroup = args.group;
if (args.ios) {
nativeGroup.collapsible = true;
// nativeGroup.collapsed = true;
} else {
nativeGroup.setExpandable(true);
// nativeGroup.collapsed;
// nativeGroup.collapsed(true);
// nativeGroup.collapsed;
}
// console.log(JSON.stringify(nativeGroup));
}
}
};
</script>
<style scoped>
.home-panel {
vertical-align: center;
font-size: 20;
margin: 15;
}
.description-label {
margin-bottom: 15;
}
</style>
My assumption was to address this in the OnGroupUpdate method, but I'm not getting where I need to be (you can see a few attempts that are commented out).
The goal is to have this view load with minimized groups so that the user can expand the different form groups that s/he wishes to work on in sequence.
Playground link: https://play.nativescript.org/?id=XLKFoC&template=play-vue&v=14
Thanks for any help
I think what you actually need is nativeGroup.setIsExpanded(false);
exports.onGroupUpdate = (args) => {
if (app.ios) {
let nativeGroup = args.group;
nativeGroup.collapsible = true;
} else {
let nativeGroup = args.group;
nativeGroup.setExpandable(true);
//in this example i only expand one group.
if (args.groupName !== "SERVICE INFORMATION") {
nativeGroup.setIsExpanded(false)
}
}
}