How to prevent SAPUI5 lifecycles from running twice? - sapui5

I have a custom control that contains a scatter plot chart. When a button is pressed, it opens a modal/dialog control that displays the chart (see image below). The only problem is that the chart is displayed twice! Upon opening the dev tools, it appears that all of the lifecycles are rendered twice. I have no idea why this happens but I need it to render only once.
Here is my main controller:
main.controller.js
jQuery.sap.registerModulePath("vizConcept.ScatterPlot", "controls/ScatterPlot");
jQuery.sap.require("vizConcept.ScatterPlot");
jQuery.sap.registerModulePath("vizConcept.ScatterPlotItem", "controls/ScatterPlot");
jQuery.sap.require("vizConcept.ScatterPlotItem");
sap.ui.define([
'jquery.sap.global',
'vizConcept/controller/BaseController',
'sap/ui/model/json/JSONModel',
'vizConcept/model/viewControls',
'sap/m/Button',
'sap/m/Dialog',
],
function (jQuery, BaseController, JSONModel, viewControls, Button, Dialog) {
"use strict";
var controls;
var mainController = BaseController.extend("vizConcept.controller.Main", {
onInit: function(oEvent) {
// Access/expose the defined model(s) configured in the Component.js or Manifest.json within the controller.
this.getView().setModel(this.getOwnerComponent().getModel("products"), "products");
var oModel = this.getView().getModel("products");
this.getView().setModel(oModel);
//
console.log('onInit');
var sUrl = "#" + this.getOwnerComponent().getRouter().getURL("page2");
//IIFE renders our chart, will need to move this in a modular way
},
onAfterRendering: function () {
this._rebindAll();
},
_rebindAll : function() {
var oModel = new sap.ui.model.json.JSONModel({
"buckets": [
{
"quarter": "Q2 2013",
"values": [
{ "name": "Segment A",
"value": "3228",
"value2": "12"
},
{
"name": "Segment B",
"value": "11752",
"value2": "37"
},
{
"name": "Segment C",
"value": "492",
"value2": "3"
},
{
"name": "Segment D",
"value": "654",
"value2": "6"
},
{
"name": "Segment E",
"value": "39165",
"value2": "167"
},
{
"name": "Segment F",
"value": "4745",
"value2": "9"
}
]
},
{
"quarter": "Q3 2013",
"values": [
{ "name": "Segment A",
"value": "6806",
"value2": "24"
},
{
"name": "Segment B",
"value": "11372",
"value2": "51"
},
{
"name": "Segment C",
"value": "2306",
"value2": "10"
},
{
"name": "Segment D",
"value": "1492",
"value2": "7"
},
{
"name": "Segment E",
"value": "33944",
"value2": "170"
},
{
"name": "Segment F",
"value": "6498",
"value2": "16"
}
]
},
{
"quarter": "Q4 2013",
"values": [
{ "name": "Segment A",
"value": "11228",
"value2": "23"
},
{
"name": "Segment B",
"value": "31324",
"value2": "83"
},
{
"name": "Segment C",
"value": "1291",
"value2": "17"
},
{
"name": "Segment D",
"value": "4350",
"value2": "17"
},
{
"name": "Segment E",
"value": "52265",
"value2": "155"
},
{
"name": "Segment F",
"value": "23785",
"value2": "66"
}
]
},
{
"quarter": "Q1 2014",
"values": [
{ "name": "Segment A",
"value": "3708",
"value2": "18"
},
{
"name": "Segment B",
"value": "20176",
"value2": "74"
},
{
"name": "Segment C",
"value": "5169",
"value2": "61"
},
{
"name": "Segment D",
"value": "31322",
"value2": "76"
},
{
"name": "Segment E",
"value": "49069",
"value2": "191"
},
{
"name": "Segment F",
"value": "8928",
"value2": "19"
}
]
},
{
"quarter": "Q2 2014",
"values": [
{ "name": "Segment A",
"value": "2950",
"value2": "26"
},
{
"name": "Segment B",
"value": "6807",
"value2": "54"
},
{
"name": "Segment C",
"value": "3789",
"value2": "110"
},
{
"name": "Segment D",
"value": "12867",
"value2": "91"
},
{
"name": "Segment E",
"value": "21411",
"value2": "128"
},
{
"name": "Segment F",
"value": "18478",
"value2": "21"
}
]
}
]
});
var oScatterPlotHolder = this.byId("RegionScatterPlotHolder");
var oScatterPlotItem = new vizConcept.ScatterPlotItem({quarter:"{quarter}", values:"{values}"});
/* new chart */
var oScatterPlot = new vizConcept.ScatterPlot({
items: {path : "/buckets", template : oScatterPlotItem}
});
//var oModel = sap.ui.getCore().getModel("growth-regions-scatter");
oScatterPlot.setModel(oModel);
oScatterPlotHolder.addItem(oScatterPlot);
$(function() {
var dlg = new sap.m.Dialog({
id: 'vizModal',
title: 'Scatter Plot Example Viz',
width : "1800px",
height : "600px",
content : [oScatterPlotHolder],
beginButton: new Button({
text: 'Close',
press: function () {
dlg.close();
}
})
});
(new sap.m.Button({
text: 'open',
type: 'Accept',
press: function() {
dlg.open();
oScatterPlotHolder.invalidate();
}
})).placeAt('content');
});
},
onToPage2 : function () {
this.getOwnerComponent().getRouter().navTo("page2");
},
});
return mainController;
});
ScatterPlot.js (custom control)
jQuery.sap.require("sap/ui/thirdparty/d3");
jQuery.sap.declare("vizConcept.ScatterPlot");
sap.ui.core.Element.extend("vizConcept.ScatterPlotItem", { metadata : {
properties : {
"quarter" : {type : "string", group : "Misc", defaultValue : null},
"values" : {type : "object", group : "Misc", defaultValue : null}
}
}});
sap.ui.core.Control.extend("vizConcept.ScatterPlot", {
metadata : {
properties: {
"title": {type : "string", group : "Misc", defaultValue : "ScatterPlot Title"}
},
aggregations : {
"items" : { type: "vizConcept.ScatterPlotItem", multiple : true, singularName : "item"}
},
defaultAggregation : "items",
events: {
"onPress" : {},
"onChange":{}
}
},
init : function() {
console.log("vizConcept.ScatterPlot.init()");
this.sParentId = "";
},
createScatterPlot : function() {
/*
* Called from renderer
*/
console.log("vizConcept.ScatterPlot.createScatterPlot()");
var oScatterPlotLayout = new sap.m.VBox({alignItems:sap.m.FlexAlignItems.Center,justifyContent:sap.m.FlexJustifyContent.Center});
var oScatterPlotFlexBox = new sap.m.FlexBox({height:"auto",alignItems:sap.m.FlexAlignItems.Center});
/* ATTENTION: Important
* This is where the magic happens: we need a handle for our SVG to attach to. We can get this using .getIdForLabel()
* Check this in the 'Elements' section of the Chrome Devtools:
* By creating the layout and the Flexbox, we create elements specific for this control, and SAPUI5 takes care of
* ID naming. With this ID, we can append an SVG tag inside the FlexBox
*/
this.sParentId=oScatterPlotFlexBox.getIdForLabel();
oScatterPlotLayout.addItem(oScatterPlotFlexBox);
return oScatterPlotLayout;
},
/**
* The renderer render calls all the functions which are necessary to create the control,
* then it call the renderer of the vertical layout
* #param oRm {RenderManager}
* #param oControl {Control}
*/
renderer : function(oRm, oControl) {
var layout = oControl.createScatterPlot();
oRm.write("<div");
oRm.writeControlData(layout); // writes the Control ID and enables event handling - important!
oRm.writeClasses(); // there is no class to write, but this enables
// support for ColorBoxContainer.addStyleClass(...)
oRm.write(">");
oRm.renderControl(layout);
oRm.addClass('verticalAlignment');
oRm.write("</div>");
},
onAfterRendering: function(){
console.log("vizConcept.ScatterPlot.onAfterRendering()");
//console.log(this.sParentId);
var cItems = this.getItems();
var data = [];
for (var i=0;i<cItems.length;i++){
var oEntry = {};
for (var j in cItems[i].mProperties) {
oEntry[j]=cItems[i].mProperties[j];
}
data.push(oEntry);
}
//console.log("Data:");
//console.log(data);
/*
* ATTENTION: See .createScatterPlot()
* Here we're picking up a handle to the "parent" FlexBox with the ID we got in .createScatterPlot()
* Now simply .append SVG elements as desired
* EVERYTHING BELOW THIS IS PURE D3.js
*/
var margin = {
top: 15,
right: 15,
bottom: 30,
left: 40
},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Our X scale
var x = d3.scale.linear()
.range([0, width-80]);
// Our Y scale
var y = d3.scale.linear()
.range([height, 0]);
// Our color bands
var color = d3.scale.ordinal()
.range(["#004460", "#0070A0", "#008BC6", "#009DE0", "#45B5E5", "8CCDE9", "#DAEBF2"]); //"#00A6ED",
// Use our X scale to set a bottom axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
// Smae for our left axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var tip = d3.select("body").append("div")
.attr("class", "sctooltip")
.style("position", "absolute")
.style("text-align", "center")
.style("width", "80px")
.style("height", "42px")
.style("padding", "2px")
.style("font", "11px sans-serif")
.style("background", "#F0F0FF")
.style("border", "0px")
.style("border-radius", "8px")
.style("pointer-events", "none")
.style("opacity", 0);
var vis = d3.select("#" + this.sParentId);
var svg = vis.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("background-color","white")
.style("font", "12px sans-serif")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain([0, d3.max(data, function (d) {
var max = d3.max(d.values, function (dd){
return(+dd.value);
})
return max;
})]);
// Our Y domain is from zero to our highest total
y.domain([0, d3.max(data, function (d) {
var max = d3.max(d.values, function (dd){
return(+dd.value2);
})
return max;
})]);
var totalval = 0;
var totalval2 = 0;
data.forEach(function (d) {
var quarter = d.quarter;
d.values.forEach(function (dd){
dd.quarter = quarter;
totalval += +dd.value;
totalval2 += +dd.value2;
});
});
var average = totalval/totalval2;
var line_data = [{"x": 0, "y": 0},{"x": (y.domain()[1]*average), "y": y.domain()[1]}];
var avgline = d3.svg.line()
.x(function(d){ return x(d.x); })
.y(function(d){ return y(d.y); })
.interpolate("linear");
svg.append("g")
.attr("class", "x axis")
.style("fill", "none")
.style("stroke", "grey")
.style("shape-rendering", "crispEdges")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis")
.style("fill", "none")
.style("stroke", "grey")
.style("shape-rendering", "crispEdges")
.call(yAxis);
//average line
svg.append("path")
.attr("class", "avgline")
.style("stroke", "#000")
.style("stroke-width", "1px")
.style("stroke-dasharray", ("4, 4"))
.attr("d", avgline(line_data));
/*
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("XXXXX");
*/
var plot = svg.selectAll(".quarter")
.data(data)
.enter().append("g");
plot.selectAll("dot")
.data(function (d) {
return d.values;
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 5)
.attr("cx", function (d){
return x(d.value);
})
.attr("cy", function (d) {
return y(d.value2);
})
.style("stroke", "#004460")
.style("fill", function (d) {
return color(d.name);
})
.style("opacity", .9)
.style("visibility", function(d){
if(+d.value != 0){
return "visible";
}else{
return "hidden";
}
})
.style("pointer-events", "visible")
.on("mouseover", function(d){
tip.transition()
.duration(200)
.style("opacity", .8);
tip.html(d.name + "<br/>" + d.quarter + "<br />" + "Avg. " +(+d.value/+d.value2).toFixed(2))
.style("left", (d3.event.pageX-40) + "px")
.style("top", (d3.event.pageY-50) + "px");
})
.on("mouseout", function(d){
tip.transition()
.duration(500)
.style("opacity", 0);
});;
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) {
return "translate(0," + i * 16 + ")";
});
legend.append("rect")
.attr("x", width - 12)
.attr("width", 12)
.attr("height", 12)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.style("font", "11px sans-serif")
.text(function (d) {
return d;
});
var avglabel = svg.append("g")
.attr("transform", "translate(" + (width-40) + ",140)");
avglabel.append("text")
.style("text-anchor", "middle")
.text("Average: " + average.toFixed(2));
}
});
Here is an example of what is the outcome of the double rendering
Edit 1 (Here is my element dom tree)

I have noticed depending on lifecycle events around rendering can be tricky, I think it's up to UI5 to determine when these run. If you only want to run once, then you can use onRoutePatternMatched() or onBeforeShow(), these are more consistent for me at least.
If you don't want to do this, you can use a global boolean and set it in onAfterRendering(), that way next time it runs it won't pass the test. Something like this:
ranOnce : false,
onAfterRendering : function(){
if(ranOnce){ break; }
// do codez
ranOnce = true;
}

Related

Google Column Graph Single Date and value showing as multiple adjucent bars

Data Table structure is as follows
{
"cols": [
{
"id": "",
"label": "Date",
"pattern": "",
"type": "date"
},
{
"id": "Col1",
"label": "Col1 Label",
"pattern": "",
"type": "number"
}
],
"rows": [
{
"c": [
{
"v": "Date(2017, 5, 27)"
},
{
"v": 213
}
]
}
]
}
H Axis options
hAxis: {
slantedText: true, slantedTextAngle: 35,
titleTextStyle: {bold: false, fontSize: 11, color: '#610B38'},
textStyle: {
bold: true, fontSize: 8, color: '#4d4d4d'
},
maxAlternation: 1,
showTextEvery: 1,
viewWindowMode : 'maximized',
gridlines:
{
count: 31
},
bar: { groupWidth: 40 }
}
But in the column graph it is displaying multiple adjacent bars looks like time instead of a single date
This issue happening only for single date.
I want to display the column chart as single bar instead of big rectangle.
Any feedback appreciated
recommend setting the min / max values on the viewWindow explicitly
keeping within a day should prevent "column overflow"
viewWindow: {
min: new Date(dateRange.min.getTime() - oneDay),
max: new Date(dateRange.max.getTime() + oneDay)
}
see following working snippet...
google.charts.load('current', {
callback: function () {
drawChart();
$(window).on('resize', drawChart);
},
packages:['corechart']
});
function drawChart() {
var data = new google.visualization.DataTable({
"cols": [
{
"id": "",
"label": "Date",
"pattern": "",
"type": "date"
},
{
"id": "Col1",
"label": "Col1 Label",
"pattern": "",
"type": "number"
}
],
"rows": [
{
"c": [
{
"v": "Date(2017, 5, 27)"
},
{
"v": 213
}
]
}
]
});
var oneDay = (1000 * 60 * 60 * 24);
var dateRange = data.getColumnRange(0);
var chart = new google.visualization.ColumnChart($('.chart')[0]);
chart.draw(data, {
hAxis: {
slantedText: true,
slantedTextAngle: 35,
textStyle: {
bold: true,
fontSize: 8,
color: '#4d4d4d'
},
viewWindow: {
min: new Date(dateRange.min.getTime() - oneDay),
max: new Date(dateRange.max.getTime() + oneDay)
}
}
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div class="chart"></div>
not much you can do for the actual size of the column
the release notes from February, 23 2016 mention...
Added options to specify bar.width, bar.gap, bar.group.width (was bar.groupWidth) and bar.group.gap.
but none have ever worked for me when only one row of data...

How to remove colon from the pie chart - Amcharts?

I just wan to remove the color from the pie chart. Please find the reference in the attachment.
You can modify the labelText property to customize the label. By default it's set to "[[title]]: [[percents]]%", so changing it to "[[percents]]%" will get what you want.
var chart = AmCharts.makeChart("chartdiv", {
// ...
"labelText": "[[percents]]%",
// ...
});
Demo below:
var chart = AmCharts.makeChart( "chartdiv", {
"type": "pie",
"theme": "light",
"labelText": "[[percents]]%",
"dataProvider": [ {
"country": "Lithuania",
"litres": 501.9
}, {
"country": "Czech Republic",
"litres": 301.9
}, {
"country": "Ireland",
"litres": 201.1
}, {
"country": "Germany",
"litres": 165.8
}, {
"country": "Australia",
"litres": 139.9
}, {
"country": "Austria",
"litres": 128.3
}, {
"country": "UK",
"litres": 99
}, {
"country": "Belgium",
"litres": 60
}, {
"country": "The Netherlands",
"litres": 50
} ],
"valueField": "litres",
"titleField": "country",
"balloon":{
"fixedPosition":true
},
"export": {
"enabled": true
}
} );
<script type="text/javascript" src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script type="text/javascript" src="//www.amcharts.com/lib/3/pie.js"></script>
<script type="text/javascript" src="//www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv" style="width: 100%; height: 400px;"></div>
I know I am late but I have searched for this for quite few days and it took me enough time to get the answer. I am answering just to help someone who searches this for their answer.
I resolved this by overriding calls backs of tooltipItem as
options: {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.labels[tooltipItem.index];
return `${data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]}`;
}
}
}
}
And if you have multiple data sets and you want to have label too, here what I did,
callbacks: {
label: function(tooltipItem, data) {
console.log(tooltipItem, data);
var datasetLabel = '';
var label = data.datasets[tooltipItem.datasetIndex].label;
return `${label} : ${data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]}`;
}
}
you can modify it accordingly.
You can this out https://jsfiddle.net/18jmzcx2/
Well fellas, You must include just this line:
pieSeries.labels.template.text = "{value.percent.formatNumber('##.0')}%";
Done!

How to display Formatted value in bubble chart using iccube reporting v6?

Using V6 reporting, I created a bubble chart with simple percentage values. I'm able to format the axis values using it's "format" property. But in the Bubble tooltip, the value is still raw value... Is there a way to show the Formatted value instead of the value ?
Here is the code of the report based on "Sales" schema to reproduce easily:
{
"classID": "ic3.ReportGuts",
"guts_": {
"schemaName": "Sales",
"cubeName": "Sales",
"themeId": "ic3-elegant",
"ic3Rev": 4015,
"layout": {
"classID": "ic3.FixedLayout",
"guts_": {
"grid": 10,
"boxes": [
{
"classID": "ic3.FixedLayoutBox",
"guts_": {
"behaviour": "Fixed Box",
"position": {
"top": 10,
"left": 10,
"width": 930,
"height": 460
},
"advanced": {
"zIndex": 15
},
"header": "#{bubbleTitle}",
"boxStyle": "zoneRequired",
"ic3_uid": "ic3-5",
"widgetAdapterUid": "w1"
}
}
]
}
},
"widgetMgr": {
"classID": "ic3.WidgetAdapterContainerMgr",
"guts_": {
"items": [
{
"classID": "ic3.GoogleChartsAdapter",
"guts_": {
"configState": 3,
"navigationGuts": {
"classID": "ic3.NavigationStrategy",
"guts_": {
"menuVisibility": {
"back": false,
"reset": false
},
"maxAxisMemberCount": 25
}
},
"dataRenderOptions": {
"chartType": {
"label": "Bubble",
"id": "bubble-google-chart",
"proto": {
"options": {
"width": "100%",
"height": "100%",
"bubble": {
"textStyle": {
"fontSize": 10
}
}
},
"chartType": "BubbleChart"
}
},
"axesConfiguration": null,
"graphsConfiguration": null,
"advanced": {
"hAxis": {
"format": "\"##.##%\""
},
"vAxis": {
"format": null
},
"legend": {
"position": "none"
},
"colorAxis": {
"colors": [
"#1F77B4",
"#FF7F0E",
"#2CA02C",
"#D62728",
"#9467BD",
"#8C564B",
"#E377C2",
"#7F7F7F",
"#BCBD22",
"#17BECF"
],
"legend": {
"position": "none"
}
},
"explorer": {},
"sizeAxis": {
"minSize": 7
},
"tooltip": {
"format": null
}
}
},
"ic3_name": "widget-12",
"ic3_eventMapper": {
"classID": "ic3.EventWidgetMapper",
"guts_": {
"__ic3_widgetEventsDescription": {}
}
},
"navigationOptions": {
"menuVisibility": {
"back": false,
"reset": false
}
},
"hooks": {
"beforeData": "/**\n * Return data object\n */\nfunction(context, data, $box) {\n //bubble on data received\n debugger\n\treturn data;\n}",
"beforeRender": "/**\n * Return patched \n * options object.\n */\nfunction(context, options) {\n //bubble before render\n\treturn options;\n}"
},
"ic3_uid": "w1",
"ic3_mdxBuilderUid": "m1"
}
}
]
}
},
"constantMgr": {
"classID": "ic3.ConstantsMgr",
"guts_": {}
},
"cssMgr": {
"classID": "ic3.CssMgr",
"guts_": {}
},
"javascriptMgr": {
"classID": "ic3.ReportJavascriptMgr",
"guts_": {
"js": "/** \n * A function called each time an event is generated. \n * \n * #param context the same object is passed between consumeEvent calls. \n * Can be used to store information. \n * { \n * $report : jQuery context of the report container \n * fireEvent : a function( name, value ) triggering an event \n * } \n * \n * #param event the event information \n * \n { \n * name : as specified in the 'Events' tab \n * value : (optional) actual event value \n * type : (optional) e.g., ic3selection \n * } \n * \n * Check the 'Report Event Names' menu for the list of available events. \n */ \n/* \nfunction consumeEvent( context, event ) { \n if (event.name == 'ic3-report-init') { \n // add your code here \n } \n} \n*/ \n"
}
},
"calcMeasureMgr": {
"classID": "ic3.CalcMeasureMgr",
"guts_": {
"measures": []
}
},
"mdxQueriesMgr": {
"classID": "ic3.MdxQueriesContainerMgr",
"guts_": {
"mdxQueries": {
"classID": "ic3.BaseContainerMgr",
"guts_": {
"items": [
{
"classID": "ic3.QueryBuilderWidget",
"guts_": {
"mode": "MDX",
"options": {
"WIZARD": {
"cubeName": null,
"measures": [],
"rows": [],
"rowsNonEmpty": false,
"columns": [],
"columnsNonEmpty": false,
"filter": []
},
"MDX": {
"statement": "with\nmember [PDM Sejours] as 0.5072 ,format_string=\"percent\"\nmember [Evo PDM] as 0.00291 ,format_string=\"percent\"\nmember [Activité Etablissements] as 8113 ,format_string=\"#,###\"\nselect \n NON EMPTY {[Measures].[PDM Sejours], [Measures].[Evo PDM] , [Measures].[Activité Etablissements]} ON COLUMNS, \nNON EMPTY {[Product].[Product].[Company].[icCube]} on ROWS \nfrom [Sales]\n"
}
},
"ic3_name": "mdx Query-0",
"ic3_uid": "m1",
"schemaSettings": {}
}
}
]
}
},
"mdxFilter": {
"classID": "ic3.BaseContainerMgr",
"guts_": {
"items": []
}
},
"actionBuilders": {
"classID": "ic3.BaseContainerMgr",
"guts_": {
"items": []
}
}
}
},
"customLocalizations": []
}
}
formatting the data for the chart, should flow through to the tooltip
not familiar with iccube, however, when loading the data, you can use google's object notation
to provide both the value (v:) and the formatted value (f:)
for example, instead of loading the following data row...
['Sub-Saharan Africa', 80, 1.023],
use object notation...
[{v: 'Sub-Saharan Africa'}, {v: 80, f: 'test 80'}, {v: 1.023, f: 'test 1.023000000'}],
the tooltip should display the value for f:
see following working snippet...
google.charts.load('current', {
callback: function () {
var data = new google.visualization.DataTable({
cols: [
{label: 'ID', type: 'string'},
{label: 'X', type: 'number'},
{label: 'Y', type: 'number'}
],
rows: [
{c:[{v: 'Sub-Saharan Africa'}, {v: 80, f: 'test 80'}, {v: 1.023, f: 'test 1.023000000'}]},
{c:[{v: 'Arab States'}, {v: 80, f: 'test 80'}, {v: 1.022, f: 'test 1.0220000000'}]},
{c:[{v: 'East Asia and the Pacific'}, {v: 80, f: 'test 80'}, {v: 1.21, f: 'test 1.2100000000'}]}
]
});
var container = document.getElementById('chart_div');
var chart = new google.visualization.BubbleChart(container);
chart.draw(data);
},
packages: ['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Firebase, Retrieving Data: Find all Titles that have 'Status' as 'Open' in Swift

Main mission is to find all Titles that have 'Status' as 'Open'
I want to get all the favorTit to an array to be used in indexPath.row
func retrieveQuery(){
var i = 0
queryRef.queryOrderedByChild("status").queryEqualToValue("Open")
.observeEventType(.ChildAdded, withBlock:
{ snapshot in
i++
self.antal = snapshot.children.allObjects.count
print(self.antal)
print(snapshot.value.objectForKey("favorTit")!)
print(i)
})
}
Firebase data
https://api.myjson.com/bins/4n5rn
{
"categories": [{
"altText": "T.ex. Städning, Disk, Tvätt, Matlagning",
"title": "Hushåll"
}, {
"altText": "T.ex. ",
"title": " "
}, {
"altText": " ",
"title": " "
}, {
"altText": " ",
"title": " "
}, {
"altText": " ",
"title": " "
}, {
"altText": " ",
"title": " "
}, {
"altText": " ",
"title": " "
}],
"favors": {
"1": {
"catId": 1,
"favorBudget": 212121,
"favorDes": "Gfdgsfdsg",
"favorDueDate": "Today",
"favorGeo": [-7.090910999999999, 107.668887],
"favorLocation": ["West Java, Indonesia"],
"favorTit": "Rätt",
"status": "Open",
"user": "2872e074-e45a-4d7a-a9c7-83fad641aa62",
"workCompletion": "In person"
},
"2": {
"catId": 1,
"favorBudget": 4000,
"favorDes": "Gfdgf",
"favorDueDate": "Today",
"favorGeo": [34.506668, -81.948334],
"favorLocation": ["Laurens County Airport, Laurens, SC 29360, USA"],
"favorTit": "Rätt",
"status": "Open",
"user": "2872e074-e45a-4d7a-a9c7-83fad641aa62",
"workCompletion": "In person"
},
"3": {
"catId": 1,
"favorBudget": 4000,
"favorDes": "Gfdgf",
"favorDueDate": "Today",
"favorGeo": [34.506668, -81.948334],
"favorLocation": ["Laurens County Airport, Laurens, SC 29360, USA"],
"favorTit": "Rätt",
"status": "Ongoing",
"user": "2872e074-e45a-4d7a-a9c7-83fad641aa62",
"workCompletion": "In person"
},
"7fd547be-7836-42e2-a74f-2f2a39baee43": {
"favorTit": "Test",
"favors": {
"furniture assmebly": {
"favorBudget": 60000,
"favorDes": "Assemly My ikea",
"favorDueDate": "Today",
"favorGeo": [39.0311755, -77.5283463],
"favorLocation": ["Polen Farm Blvd, Ashburn, VA 20148, USA"],
"favorTit": "Den ska bli fixad kom snabbt",
"workCompletion": "In person"
},
"handyman": {
"favorBudget": 43434,
"favorDes": "Install TV-Mount",
"favorDueDate": "Today",
"favorGeo": [49.0068901, 8.4036527],
"favorLocation": ["Karlsruhe, Germany"],
"favorTit": "JAllah",
"workCompletion": "In person"
},
"photography": {
"favorBudget": 6000,
"favorDes": "Jag vill ha ett album med bilder på mig och omgivningen💪 Du ska inte säga mycket under bröllopet men det ska vara vackra bilder",
"favorDueDate": "Within a week",
"favorGeo": [55.6178043, 12.98939],
"favorLocation": ["Krankajen 36, 211 12 Malmö, Sverige"],
"favorTit": "take a photo of my wedding",
"workCompletion": "In person"
},
"trädgård": {
"favorBudget": 2000,
"favorDes": "Jag vill ha den klippt med en sax",
"favorDueDate": "Within a week",
"favorGeo": [35.86166, 104.195397],
"favorLocation": ["China"],
"favorTit": "Klipp min gräsmatta",
"workCompletion": "In person"
}
},
"status": "Done"
}
},
"users": {
"2872e074-e45a-4d7a-a9c7-83fad641aa62": {
"email": "fille382#gmail.com",
"name": "lol",
"phone": "123567890",
"provider": "password"
},
"5f0fb39e-620a-4cd1-9e6a-2b7ae9baaf71": {
"email": "lol#gmail.com",
"name": "Gunn Bellander",
"phone": "0735158548",
"provider": "password"
},
"7fd547be-7836-42e2-a74f-2f2a39baee43": {
"about": "Johan my name is Filip Bellander and i am creating this beautiful app right now the about page is here so the user can quickly reach its target by writing about himself and his completions ",
"comments": {
"4354352": {
"comment": "He did everything even cleaned up after himself",
"stars": 3,
"tasktitle": "Do everything"
},
"423489054": {
"comment": "Yes very nice",
"stars": 1,
"tasktitle": "call my phone"
},
"5435486956": {
"comment": "It was very clean",
"stars": 3,
"tasktitle": "Clean room"
},
"54643654654": {
"comment": "He did a great job wiping all whipcream from the luggage",
"stars": 4,
"tasktitle": "Whipe dat"
}
},
"completed": 90,
"email": "test#gmail.com",
"name": "Filip Bellander",
"phone": "0735158548",
"posted": 81,
"provider": "password",
"rating": 5,
"reviews": 1337,
"skills": "App programmer, Visual designer DriversDriverslicenseDriverslicenseDriverslicenseDriverslicenselicense no"
},
"bdc6c3f8-764a-4468-825a-408f53695b24": {
"email": "Elisabet.bellander#gmail.com",
"name": "Elisabet Bellander",
"phone": "0721645504",
"provider": "password"
}
}
}
From your code you should already have the Open Titles array in your snapshot.
if let result = snapshot.children.allObjects as? [FIRDataSnapshot] {
self.items = result
}
Then your TableView will depend on how you are implementing it. There is some examples here and here.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
let cellDict = items[indexPath.row]
...
return cell
}
I was able to make an array by inserting the string at index 0 everytime it runs by using the code below
queryRef.queryOrderedByChild("status").queryEqualToValue("Open")
.observeEventType(.ChildAdded, withBlock:
{ snapshot in
self.antal = snapshot.children.allObjects.count
print(self.antal)
var titles = snapshot.value.objectForKey("favorTit") as! String
var description = snapshot.value.objectForKey("favorDes") as! String
var budget = snapshot.value.objectForKey("favorBudget") as! Double
self.openTitles.insert(titles, atIndex: 0)
self.openDescription.insert(description, atIndex: 0)
self.openBudget.insert(budget, atIndex: 0)
print(self.openTitles)
print(self.openDescription)
})

how to code sap.m.sample.ListGrouping by using js view in openui5

hi i need List grouping control by using js view.but openui5 provides code by using xml view.
https://openui5.hana.ondemand.com/explored.html#/sample/sap.m.sample.ListGrouping/preview
how to convert this code into js view and how to make ListGrouping able to selection for both element level and group level and change this as dropdown box
List.view.xml
<mvc:View
controllerName="sap.m.sample.ListGrouping.List"
xmlns:l="sap.ui.layout"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<List
items="{
path: '/ProductCollection',
sorter: {
path: 'SupplierName',
descending: false,
group: true
},
groupHeaderFactory: '.getGroupHeader'
}"
headerText="Products" >
<StandardListItem
title="{Name}"
description="{ProductId}"
icon="{ProductPicUrl}"
iconDensityAware="false"
iconInset="false" />
</List>
</mvc:View>
List.controller.js
sap.ui.define([
'jquery.sap.global',
'sap/m/GroupHeaderListItem',
'sap/ui/core/mvc/Controller',
'sap/ui/model/json/JSONModel'
], function(jQuery, GroupHeaderListItem, Controller, JSONModel) {
"use strict";
var ListController = Controller.extend("sap.m.sample.ListGrouping.List", {
onInit : function (evt) {
// set explored app's demo model on this sample
var oModel = new JSONModel(jQuery.sap.getModulePath("sap.ui.demo.mock", "/products.json"));
this.getView().setModel(oModel);
},
getGroupHeader: function (oGroup){
return new GroupHeaderListItem( {
title: oGroup.key,
upperCase: false
} );
}
});
return ListController;
});
how to write the same code by using js view
I have tried like as follows, but i am getting Error: Missing template or factory function for aggregation items of Element sap.m.List#__list0 !
List.view.js
sap.ui.jsview("oui5mvc.List", {
getControllerName : function() {
return "oui5mvc.List";
},
createContent : function(oController) {
odbbshiftGlobalId = this.getId();
var oMyFlexbox = new sap.m.FlexBox({
items: [
oList = new sap.m.List({
width: '500px',
group: true,
groupHeaderFactory: '.getGroupHeader',
items: [
]
}),
]
});
oMyFlexbox.placeAt(this.getId()).addStyleClass("tes");
}
});
List.controller.js
sap.ui.controller("oui5mvc.List", {
onInit: function() {
var data = {
"ProductCollection": [
{
"ProductId": "1239102",
"Name": "Power Projector 4713",
"Category": "Projector",
"SupplierName": "Titanium",
"Description": "A very powerful projector with special features for Internet usability, USB",
"WeightMeasure": 1467,
"WeightUnit": "g",
"Price": 856.49,
"CurrencyCode": "EUR",
"Status": "Available",
"Quantity": 3,
"UoM": "PC",
"Width": 51,
"Depth": 42,
"Height": 18,
"DimUnit": "cm",
"ProductPicUrl": "https://openui5.hana.ondemand.com/test-resources/sap/ui/demokit/explored/img/HT-6100.jpg"
},
{
"ProductId": "2212-121-828",
"Name": "Gladiator MX",
"Category": "Graphics Card",
"SupplierName": "Technocom",
"Description": "Gladiator MX: DDR2 RoHS 128MB Supporting 512MB Clock rate: 350 MHz Memory Clock: 533 MHz, Bus Type: PCI-Express, Memory Type: DDR2 Memory Bus: 32-bit Highlighted Features: DVI Out, TV Out , HDTV",
"WeightMeasure": 321,
"WeightUnit": "g",
"Price": 81.7,
"CurrencyCode": "EUR",
"Status": "Discontinued",
"Quantity": 10,
"UoM": "PC",
"Width": 34,
"Depth": 14,
"Height": 2,
"DimUnit": "cm",
"ProductPicUrl": "https://openui5.hana.ondemand.com/test-resources/sap/ui/demokit/explored/img/HT-1071.jpg"
},
{
"ProductId": "K47322.1",
"Name": "Hurricane GX",
"Category": "Graphics Card",
"SupplierName": "Red Point Stores",
"Description": "Hurricane GX: DDR2 RoHS 512MB Supporting 1024MB Clock rate: 550 MHz Memory Clock: 933 MHz, Bus Type: PCI-Express, Memory Type: DDR2 Memory Bus: 64-bit Highlighted Features: DVI Out, TV-In, TV-Out, HDTV",
"WeightMeasure": 588,
"WeightUnit": "g",
"Price": 219,
"CurrencyCode": "EUR",
"Status": "Out of Stock",
"Quantity": 25,
"UoM": "PC",
"Width": 34,
"Depth": 14,
"Height": 2,
"DimUnit": "cm",
"ProductPicUrl": "https://openui5.hana.ondemand.com/test-resources/sap/ui/demokit/explored/img/HT-1072.jpg"
},
],
"ProductCollectionStats": {
"Counts": {
"Total": 14,
"Weight": {
"Ok": 7,
"Heavy": 5,
"Overweight": 2
}
},
"Groups": {
"Category": {
"Projector": 1,
"Graphics Card": 2,
"Accessory": 4,
"Printer": 2,
"Monitor": 3,
"Laptop": 1,
"Keyboard": 1
},
"SupplierName": {
"Titanium": 3,
"Technocom": 3,
"Red Point Stores": 5,
"Very Best Screens": 3
}
},
"Filters": [
{
"type": "Category",
"values": [
{
"text": "Projector",
"data": 1
},
{
"text": "Graphics Card",
"data": 2
},
{
"text": "Accessory",
"data": 4
},
{
"text": "Printer",
"data": 2
},
{
"text": "Monitor",
"data": 3
},
{
"text": "Laptop",
"data": 1
},
{
"text": "Keyboard",
"data": 1
}
]
},
{
"type": "SupplierName",
"values": [
{
"text": "Titanium",
"data": 3
},
{
"text": "Technocom",
"data": 3
},
{
"text": "Red Point Stores",
"data": 5
},
{
"text": "Very Best Screens",
"data": 3
}
]
}
]
}
};
var oTemplate11 = new sap.m.StandardListItem({title : "{Name}"});
oList.setModel(new sap.ui.model.json.JSONModel(data));
oList.bindItems("/ProductCollection");
oList.placeAt('content');
},
getGroupHeader: function (oGroup){
return new sap.m.GroupHeaderListItem( {
title: oGroup.key,
upperCase: false
});
},
});
Your call to bind items to the list is not entirely correct.
The method takes an object with binding information as parameter instead of just the path to the model property. See the documentation for bindItems and bindAggregation in general.
In your case it should look like
oList.bindItems({
path: "/ProductCollection",
template: new sap.m.StandardListItem({
title: "{Name}",
description: "{ProductId}",
icon: "{ProductPicUrl}"
})
});