SAPUI5 Chart 50053 incomplete dimension binding - charts

Experimenting with charts for the first time, trying to setup a simple chart here which is simply based on 3 values from the table seen in the snapshot below (chart positioned exactly below): ​Type ID, Avg Gross Turnaround Time (1st occurence), Avg Net Turnaround Time (1st occurence) and (as visible) I get the aforementioned error.
Chart Error
Of course, before posting I had a look to similar questions and in all cases the solution was the same, that the values provided in the measures/dimensions of the dataset must be the same (makes sense) with the ones provided with the FeedItems method. Problem is that I have them right. Thought about a syntax error within the dataset/feed definitions (eg, single instead of double quotes). Tried numerous combinations so far, result always the same, so I ended up leaving the only syntax that wasn't giving me blue warnings in the editor. (Extremely short) controller code follows below. Does this ring any bells?
​
​Regards
​Greg
var oVizFrame = this.getView().byId("idStackedChart");
oVizFrame.setVizProperties({
plotArea: {
colorPalette: d3.scale.category20().range(),
dataLabel: {
showTotal: true
}
},
tooltip: {
visible: true
},
title: {
text: "Stacked Bar Chart"
}
});
var oDataset = new sap.viz.ui5.data.FlattenedDataset({
dimensions: [{
name: "Type",
value: "{AgrTypeid}"
}],
measures: [{
name: "Gross Turnaround",
value: "{Yr1Avggta}"
}, {
name: "Net Turnaround",
value: "{Yr1Avgnta}"
}],
data: {
path: "/Agreement_Summary"
}
});
oVizFrame.setDataset(oDataset);
oVizFrame.setModel();
var oFeedValueAxis = new sap.viz.ui5.controls.common.feeds.FeedItem({
uid: "valueAxis",
type: "Measure",
values: ["Yr1Avggta"]
}),
oFeedValueAxis1 = new sap.viz.ui5.controls.common.feeds.FeedItem({
uid: "valueAxis",
type: "Measure",
values: ["Yr1Avgnta"]
}),
oFeedCategoryAxis = new sap.viz.ui5.controls.common.feeds.FeedItem({
uid: "categoryAxis",
type: "Dimension",
values: ["AgrTypeid"]
});
oVizFrame.addFeed(oFeedValueAxis);
oVizFrame.addFeed(oFeedValueAxis1);
oVizFrame.addFeed(oFeedCategoryAxis);

Alright, it works a little different, here's the correct feed code:
var oFeedValueAxis = new sap.viz.ui5.controls.common.feeds.FeedItem({
uid: "valueAxis",
type: "Measure",
values: ["Type"] //--> should be same as Measure name
}),
oFeedValueAxis1 = new sap.viz.ui5.controls.common.feeds.FeedItem({
uid: "valueAxis",
type: "Measure",
values: ["Gross Turnaround"] //--> should be same as Measure name
}),
oFeedCategoryAxis = new sap.viz.ui5.controls.common.feeds.FeedItem({
uid: "categoryAxis",
type: "Dimension",
values: ["Net Turnaround"] //--> should be same as Dimension name
});

Related

ApexCharts: Hide every nth label in chart

I would like to hide some of the labels from my chart made with ApexCharts.js. I am coming from Frappé Charts, which has a feature called "continuity." It allows you to hide labels if they do not comfortably fit, because the chart is a timeseries chart.
My ApexChart looks like this:
I would like to remove many of the dates, but still have them appear in the tooltip. I was able to do this in Frappé Charts and it looked like this:
Here's my code for the Apex chart:
var options = {
chart: {
animations: { enabled: false },
toolbar: { show: false },
zoom: { enabled: false },
type: 'line',
height: 400,
fontFamily: 'PT Sans'
},
stroke: {
width: 2
},
theme: {
monochrome: {
enabled: true,
color: '#800000',
shadeTo: 'light',
shadeIntensity: 0.65
}
},
series: [{
name: 'New Daily Cases',
data: [2,0,0,0,0,0,0,1,0,1,0,7,1,1,1,8,0,11,2,9,8,21,17,28,24,20,38,39,36,21,10,49,45,44,52,74,31,29,43,28,39,58,30,47,50,31,28,79,39,54,55,33,42,39,41,52,25,30,37,26,30,35,42,64,46,25,35,45,56,45,64,34,34,32,40,65,56,64,55,37,61,51,70,81,76,64,71,61,56,52,106,108,104,33,57,82,71,67,68,63,71,32,70,65,98,52,72,87,66,85,90,47,164,123,180,119,85,66,122,65,155,191,129,144,175,224,234,240,128,99,141,131,215,228,198,152,126,201,92,137,286,139,236,238,153,170,106,61]
}],
labels: ['February 28','February 29','March 1','March 2','March 3','March 4','March 5','March 6','March 7','March 8','March 9','March 10','March 11','March 12','March 13','March 14','March 15','March 16','March 17','March 18','March 19','March 20','March 21','March 22','March 23','March 24','March 25','March 26','March 27','March 28','March 29','March 30','March 31','April 1','April 2','April 3','April 4','April 5','April 6','April 7','April 8','April 9','April 10','April 11','April 12','April 13','April 14','April 15','April 16','April 17','April 18','April 19','April 20','April 21','April 22','April 23','April 24','April 25','April 26','April 27','April 28','April 29','April 30','May 1','May 2','May 3','May 4','May 5','May 6','May 7','May 8','May 9','May 10','May 11','May 12','May 13','May 14','May 15','May 16','May 17','May 18','May 19','May 20','May 21','May 22','May 23','May 24','May 25','May 26','May 27','May 28','May 29','May 30','May 31','June 1','June 2','June 3','June 4','June 5','June 6','June 7','June 8','June 9','June 10','June 11','June 12','June 13','June 14','June 15','June 16','June 17','June 18','June 19','June 20','June 21','June 22','June 23','June 24','June 25','June 26','June 27','June 28','June 29','June 30','July 1','July 2','July 3','July 4','July 5','July 6','July 7','July 8','July 9','July 10','July 11','July 12','July 13','July 14','July 15','July 16','July 17','July 18','July 19','July 20','July 21','July 22','July 23','July 24'],
xaxis: {
tooltip: { enabled: false }
},
}
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="chart"></div>
And here's my code for the Frappé Chart if it helps:
const data = {
labels: ['February 28','February 29','March 1','March 2','March 3','March 4','March 5','March 6','March 7','March 8','March 9','March 10','March 11','March 12','March 13','March 14','March 15','March 16','March 17','March 18','March 19','March 20','March 21','March 22','March 23','March 24','March 25','March 26','March 27','March 28','March 29','March 30','March 31','April 1','April 2','April 3','April 4','April 5','April 6','April 7','April 8','April 9','April 10','April 11','April 12','April 13','April 14','April 15','April 16','April 17','April 18','April 19','April 20','April 21','April 22','April 23','April 24','April 25','April 26','April 27','April 28','April 29','April 30','May 1','May 2','May 3','May 4','May 5','May 6','May 7','May 8','May 9','May 10','May 11','May 12','May 13','May 14','May 15','May 16','May 17','May 18','May 19','May 20','May 21','May 22','May 23','May 24','May 25','May 26','May 27','May 28','May 29','May 30','May 31','June 1','June 2','June 3','June 4','June 5','June 6','June 7','June 8','June 9','June 10','June 11','June 12','June 13','June 14','June 15','June 16','June 17','June 18','June 19','June 20','June 21','June 22','June 23','June 24','June 25','June 26','June 27','June 28','June 29','June 30','July 1','July 2','July 3','July 4','July 5','July 6','July 7','July 8','July 9','July 10','July 11','July 12','July 13','July 14','July 15','July 16','July 17','July 18','July 19','July 20','July 21','July 22','July 23','July 24'],
datasets: [{
name: 'Cumulative Cases',
values: [2,0,0,0,0,0,0,1,0,1,0,7,1,1,1,8,0,11,2,9,8,21,17,28,24,20,38,39,36,21,10,49,45,44,52,74,31,29,43,28,39,58,30,47,50,31,28,79,39,54,55,33,42,39,41,52,25,30,37,26,30,35,42,64,46,25,35,45,56,45,64,34,34,32,40,65,56,64,55,37,61,51,70,81,76,64,71,61,56,52,106,108,104,33,57,82,71,67,68,63,71,32,70,65,98,52,72,87,66,85,90,47,164,123,180,119,85,66,122,65,155,191,129,144,175,224,234,240,128,99,141,131,215,228,198,152,126,201,92,137,286,139,236,238,153,170,106,61],
chartType: 'line'
}]
}
const chart = new frappe.Chart('#chart', {
data: data,
type: 'line',
height: 250,
animate: false,
barOptions: {
spaceRatio: 0.25
},
colors: ['#800000'],
tooltipOptions: {
formatTooltipY: d => d.toLocaleString()
},
axisOptions: {
xAxisMode: 'tick',
xIsSeries: true
},
lineOptions: {
hideDots: true,
regionFill: true
}
})
<script src="https://cdn.jsdelivr.net/npm/frappe-charts#1.5.2/dist/frappe-charts.min.iife.min.js"></script>
<div id="chart"></div>
I've tried using the formatter callback function to return only every 10th value, but things get all out of position and the tooltips don't work. I get similar problems returning an empty string or a space for the values I wish to exclude (but still include in the tooltip).
What I do is calculate the ratio between the area's width and the number of ticks, and if that ratio is above a certain number, I add a classname to the chart or it's wrapper and there I write:
.apexcharts-xaxis-label{
display: none;
&:nth-child(5n){ display:revert; }
}
So every 5th label is shown and the rest are hidden.
You can also set up a resizeObserver to add/remove the special class.
This require the below config to be given to the chart:
xaxis: {
labels: {
rotate: 0, // no need to rotate since hiding labels gives plenty of room
hideOverlappingLabels: false // all labels must be rendered
}
}
You can try 2 things.
xaxis: {
type: 'datetime',
}
You can convert the x-axis to datetime and labels will align as shown below
Or
You can stop rotation of the x-axis labels using
xaxis: {
labels: {
rotate: 0
}
}
which produces the following result.
Vsync answer have not worked for me. It needed a little modification:
.apexcharts-xaxis-texts-g text[id^='SvgjsText'] {
display: none;
}
.apexcharts-xaxis-texts-g text[id^='SvgjsText']:nth-of-type(5n) {
display: revert;
}
labels: ['',this.props.itemNames], //"(labels: [the label , the label below])"

ExtJS MultiSelect Edit - Not working for multi value selection

I have a GridEditPanel where the 1st column is a combobox with multiSelect. The values are being loaded correctly from the DB and is being written in the DB correctly as well. In the event where the the combobox has a single value, the drop-down highlights the value correctly as well.
The issue is when the combobox has multiple values, it displays the values correctly, however during edit the multiple values are not selected.
Model:
extend: 'Ext.data.Model',
idProperty: 'contactTypeID',
fields: [
{
name: 'contactTypeID',
type: 'string'
},
{
name: 'contactType',
type: 'string'
}
],
View GridEditPanel
emptyText: "There are no contacts.",
insertErrorText: 'Please finish editing the current contact before inserting a new record',
addButtonText: 'Add Contact',
itemId: 'contacts',
viewConfig: {
deferEmptyText: false
},
minHeight: 130,
initComponent: function () {
var me = this,
contactTypes;
// Creating store to be referenced by column renderer
contactTypes = Ext.create('Ext.data.Store', {
model: '********',
autoLoad: true,
listeners: {
load: function () {
me.getView().refresh();
}
}
});
this.columns = [
{
text: 'Contact Role',
dataIndex: 'contactRoleID',
flex: 1,
renderer: function (value) {
// Lookup contact type to get display value
//If a contact has multiple roles, use split by ',' to find display values.
if (value.includes(',')) {
var a = value.split(','), i, contTypeIds = [];
var contTypes = new Array();
for (i = 0; i < a.length; i++) {
contTypeIds.push(a[i]);
contTypes.push(contactTypes.findRecord('contactTypeID', a[i], 0, false, false, true).get('contactType'));
}
console.log('Multi Render Return Value: ' + contTypes);
return contTypes;
}
else {//if not a contact will only have one role.
var rec = contactTypes.findRecord('contactTypeID', value, 0, false, false, true); // exact match
console.log('Single Render Return Value: ' + rec.get('contactType'));
return rec ? rec.get('contactType') : '<span class="colselecttext">Required</span>';
}
},
align: 'center',
autoSizeColumn: true,
editor: {
xtype: 'combobox',
store: contactTypes,
multiSelect: true,
delimiter: ',',
forceSelection: true,
queryMode: 'local',
displayField: 'contactType',
valueField: 'contactTypeID',
allowBlank: false
}
},
I cannot see the model of GridEditPanel, but I assume you are using the wrong field type, string instead of array (Have a look at the converter function, maybe it will help you to fix the problem). I wrote a small post in my blog about multiSelect combobox editor in editable grid. The sample works with v4.2
Hope it will help you to fix the bug.

Highcharts custom error handler

We are using highcharts to plot multiple charts on a single HTML page.
However one/some of the chart throw highchart error and we like to capture those error and show different error to user.
For this highcharts do provide custom error handler. But this custom error handler does not provide information about specific chart throwing that error.
Here that JS Fiddle provided by highcharts, which works fine for a chart :
Highcharts.error = function (code, true) {
// See
https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
Highcharts.charts[0].renderer
.text('Chart error ' + code)
.attr({
fill: 'red',
zIndex: 20
})
.add()
.align({
align: 'center',
verticalAlign: 'middle'
}, null, 'plotBox');
};
http://jsfiddle.net/gh/get/library/pure/highslide-software/highcharts.com/tree/master/samples/highcharts/chart/highcharts-error/
Any idea how can I use this custom error handler per chart?
I'm using new Highcharts.Charts(options) to create new chart, but don't see way to specify error handler per chart.
Additional info: Charts are refreshed/appended using data through APIs. User that configures chart also configures refresh interval and query to use for chart.
Error handling in HighCharts does not make much sense. It would make more sense to pass the chart instance to Highcharts.error (like Kamil Kulig wrote) or to have an error event in chart.events. Anyways
here is a solution I came up with:
Create an array of errors:
var chartErrors = [];
Create an error handler which will push errors into the chartErrors. Error objects I'm making look like this: {"chartIndex": <chart index>, "errorCode": <error code>}. All charts are added to the Highcharts.charts array when they are created so we can use Highcharts.charts.length - 1 for the chartIndex.
Highcharts.error = function (code) {
// See https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
chartErrors.push({"chartIndex": Highcharts.charts.length - 1, "errorCode":code});
};
After initiating all charts we will have an array of errors. We can call forEach on this array and handle errors the way we want.
chartErrors.forEach(function(c) {
Highcharts.charts[c.chartIndex].renderer
.text('Chart error ' + c.errorCode)
.attr({
fill: 'red',
zIndex: 20
})
.add()
.align({
align: 'center',
verticalAlign: 'middle'
}, null, 'plotBox');
});
Working example:
Note: I've wrapped the code in a self invoking function to prevent leaking variables to global scope.
(function() {
var chartErrors = [];
Highcharts.error = function (code) {
// See https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
chartErrors.push({"chartIndex": Highcharts.charts.length - 1, "errorCode":code});
};
Highcharts.chart('container1', {
title: {
text: 'Demo of Highcharts error handling'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May']
},
yAxis: {
type: 'logarithmic',
min: 0
},
series: [{
data: [1, 3, 2],
type: 'column'
}]
});
Highcharts.chart('container2', {
title: {
text: 'Solar Employment Growth by Sector, 2010-2016'
},
subtitle: {
text: 'Source: thesolarfoundation.com'
},
yAxis: {
title: {
text: 'Number of Employees'
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
plotOptions: {
series: {
label: {
connectorAllowed: false
},
pointStart: 2010
}
},
series: [{
name: 'Installation',
data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
}, {
name: 'Manufacturing',
data: [24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]
}, {
name: 'Sales & Distribution',
data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]
}, {
name: 'Project Development',
data: [null, null, 7988, 12169, 15112, 22452, 34400, 34227]
}, {
name: 'Other',
data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
}],
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
});
Highcharts.chart('container3', {
title: {
text: 'Demo of Highcharts error handling'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May']
},
yAxis: {
type: 'logarithmic',
min: 0
},
series: [{
data: [1, 3, 2],
type: 'column'
}]
});
chartErrors.forEach(function(e) {
Highcharts.charts[e.chartIndex].renderer
.text('Chart error ' + e.errorCode)
.attr({
fill: 'red',
zIndex: 20
})
.add()
.align({
align: 'center',
verticalAlign: 'middle'
}, null, 'plotBox');
});
})();
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container1" style="height: 400px"></div>
<div id="container2" style="height: 400px"></div>
<div id="container3" style="height: 400px"></div>
Highcharts error function is not adjusted to have a chart context as an argument, because it can be executed in different contexts too.
For example: error number 16 occurs when Highcharts/Highstock is loaded second time in the same page. It has nothing to do with the chart, because it depends on script importing only.
The workaround I found requires some searching and and a little bit of coding.
Refer to this live demo: http://jsfiddle.net/kkulig/a8nun9aL/
I found the place in the code responsible for throwing the error 10 (the one you used in your example). I overwrote this function (see this doc page for more information about overwriting in Highcharts: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts) and added a chart variable (from Highcharts.Axis.prototype.setTickInterval scope) as the third argument:
if (
axis.positiveValuesOnly &&
!secondPass &&
Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0
) { // #978
H.error(10, 1, chart); // Can't plot negative values on log axis // MODIFIED LINE
}
It should be done for all errors you want to custom handle.
Now it can be used in custom Highcharts.error function:
Highcharts.error = function(code, stop, chart) {
// See https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
Highcharts.charts[0].renderer
.text('Chart error ' + code + " on chart titled: " + chart.title.textStr)
(...)
You can add your own property in chart constructor options and find it in chart.options object.

Typeahead/Bloodhound dupDetector for Multiple Datasets

I have two Bloodhound datasets, a local and a remote. The local data will always be duplicated in the remote results. I've found the dupDetector method mentioned in Typeahead documentation, but it seems this method only works if both my local and remote datasets are built within the same Bloodhound object.
Here's my code. Is there a way to filter duplicates across multiple Bloodhound datasets?
var local_props = new Bloodhound({
datumTokenizer: function (p) {
return Bloodhound.tokenizers.whitespace(p.name);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: portfolio_props,
limit: 100
});
var remote_props = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: 'properties/searchPropertiesLike?substr=%QUERY',
dupDetector: function(remoteMatch, localMatch) {
return remoteMatch.name === localMatch.name;
},
limit: 100
});
local_props.initialize();
remote_props.initialize();
$('#typeahead-property').typeahead({
hint: true,
highlight: true,
minLength: 2
},
{
name: 'port_properties',
displayKey: 'name',
source: local_props.ttAdapter(),
templates: {
header: '<h3>Your Portfolio Properties</h3>'
}
},
{
name: 'dir_properties',
displayKey: 'name',
source: remote_props.ttAdapter(),
templates: {
header: '<h3>Properties Directory</h3>'
}
});

Fit Mapbox map to boundaries of dynamic markers

I try to create a map with mapbox, that features dynamic markers. I want the map to automatically fit to the markers, as described here: https://www.mapbox.com/mapbox.js/example/v1.0.0/fit-map-to-markers/. Unfortunately I can't get it working.
The code also contains the feature markers as links (https://www.mapbox.com/mapbox.js/example/v1.0.0/markers-as-links/), which works great – but as JS- and Mapbox-Noob, I can't figure out how to combine both.
The code right now is this:
<div class='start-map'>
<div id='map'></div>
<script>
var map = L.mapbox.map('map', 'XXX', {zoomControl: false})
.setView([-20.1908, -67.5893], 3);
var geoJson = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-67.5893, -20.1908]
},
properties: {
title: 'Salar de Uyuni',
'marker-size': 'small',
'marker-color': '#fff',
url: 'http://localhost'
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [170.2, -43.4764]
},
properties: {
title: 'Franz Josef Glacier',
'marker-size': 'small',
'marker-color': '#fff',
url: 'http://localhost'
}
}
]
};
map.featureLayer.setGeoJSON(geoJson);
map.featureLayer.on('click', function(e) {
e.layer.unbindPopup();
window.open(e.layer.feature.properties.url);
});
geoJson.on('ready', function() {
// featureLayer.getBounds() returns the corners of the furthest-out markers,
// and map.fitBounds() makes sure that the map contains these.
map.fitBounds(geoJson.getBounds());
});
</script>
</div>
Here's corrected code: https://gist.github.com/tmcw/10203442
Issues in this example:
geoJson is a simple JavaScript object, not a layer, so you can't call methods on it.
If you're adding custom data to a map, create a new L.mapbox.featureLayer rather than using map.featureLayer
If you're calling .setGeoJSON() you do not have to wait for the .on('ready' event.