How to concatenate multiple property bindings - sapui5

How can we concatenate data in binding
JSON
{
"firstName": "John",
"lastName": "Doe",
"birthday": {
"day": "01",
"month": "05",
"year": "1982"
},
"address": [
{
"city": "Heidelberg"
}
],
"enabled": "true"
}
I am able to bind single property to label
txt.bindText("data>/birthday/year/");
But I have do display date for that I am trying to concatenate
var dData = "data>/birthday/day/" + " : "+"data>/birthday/month/" + " : " + "data>/birthday/year/";
It is not working. What is proper way to write?

Define a formatter function. Please check and run the code snippet.
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-theme="sap_bluecrystal" data-sap-ui-libs="sap.m,sap.ui.commons"></script>
<script id="view1" type="sapui5/xmlview">
<mvc:View xmlns:core="sap.ui.core" xmlns:layout="sap.ui.commons.layout" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.ui.commons" controllerName="my.own.controller" xmlns:html="http://www.w3.org/1999/xhtml">
<layout:VerticalLayout id="vl">
</layout:VerticalLayout>
</mvc:View>
</script>
<script>
sap.ui.controller("my.own.controller", {
onInit: function() {
var data = {
"firstName": "John",
"lastName": "Doe",
"birthday": {
"day": "01",
"month": "05",
"year": "1982"
},
"address": [{
"city": "Heidelberg"
}],
"enabled": "true"
};
var oText = new sap.m.Text();
oText.bindProperty("text", {
parts: [
"data>/birthday/year/",
"data>/birthday/day/",
"data>/birthday/month/"
],
formatter: function(year, day, month) {
return day + ":" + month + ":" + year;
}
});
var oVl = this.getView().byId("vl");
oVl.addContent(oText);
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData(data);
sap.ui.getCore().setModel(oModel, "data");
},
});
var myView = sap.ui.xmlview("myView", {
viewContent: jQuery('#view1').html()
}); //
myView.placeAt('content');
</script>
</head>
<body class='sapUiBody'>
<div id='content'></div>
</body>

In XMLViews you don't even need a formatter. It's as easy as:
<m:Text text="{data>/birthday/day} : {data>/birthday/month} : {data>/birthday/year}" />
Not quite sure if there is a short syntax like this for JSViews as well.
Furthermore you could make use of the UI5 Type System to get your date properly formatted for every locale.

Related

How to change aggregation binding path dynamically when pressing a button

<mvc:View xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
controllerName="z.controller.Main">
<Page id="page" title="ComboBox" >
<content>
<ComboBox name="Drop-down List" id="box0" items="{/country}">
<items >
<core:Item key="{Key}" text="{Name}" id="item0"/>
</items>
</ComboBox>
</content>
<Button text="Change" press="onChange"/>
</Page>
</mvc:View>
model1.json
{
"country": [{
"Key": "IN",
"Name": "India"
},
{
"Key": "US",
"Name": "USA"
},{
"Key": "UK",
"Name": "United Kingdom"
}]
}
model2.json
{
"country2": [{
"Key": "BD",
"Name": "Bangladesh"
},
{
"Key": "CA",
"Name": "Canada"
},{
"Key": "FR",
"Name": "France"
}]
}
Main.controller.js
sap.ui.define([
'sap/ui/core/mvc/Controller',
"sap/ui/model/json/JSONModel",
"z/models/model"
], function(Controller,JSONModel, localModel) {
'use strict';
return Controller.extend("z.controller.Main",{
onInit: function(){
var oModel = localModel.createJSONModel("models/data/model1.json");
sap.ui.getCore().setModel(oModel);
var oModel2 = localModel.createJSONModel("models/data/model2.json");
sap.ui.getCore().setModel(oModel2, "model2");
},
onChange: function(){
}
})
});
controller.js
sap.ui.define([
'sap/ui/core/mvc/Controller',
"sap/ui/model/json/JSONModel"
], function(Controller,JSONModel) {
'use strict';
return {
createJSONModel: function(filePath){
var oModel = new JSONModel();
oModel.loadData(filePath);
return oModel;
}
}
});
I have two JSON models and building a combo list using aggregation binding(items="{/country}") by reading data from model1. I am trying to fill the same Combolist by reading the data from the model2 on a button press event.
You can switch the model by using it as a prefix model2> (as you have defined in the Main.controller.js in the onInit method) in the binding path:
onChange: function() {
var oComboBox = this.getView().byId("box0");
oComboBox.bindItems("model2>/country2", {
template: this.getView().byId("item0")
});
}
And change the aggregation from items to dependents.
<ComboBox name="Drop-down List" id="box0" items="{/country}">
<dependents>
<core:Item key="{Key}" text="{Name}" id="item0"/>
</dependents>
</ComboBox>

How to get the aggregated values of an OData (V2) property?

I want to display a chart (sap.viz.ui5.controls.VizFrame) that visualizes data from an OData Service. However, the data of the service has to be manipulated. See the example below:
Main.view.xml
<mvc:View controllerName="demo.chart.controller.Main" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m">
<Shell id="shell">
<App id="app">
<pages>
<Page id="page" title="Chart Demo">
<content></content>
</Page>
</pages>
</App>
</Shell>
</mvc:View>
Chart.fragment.xml
<c:FragmentDefinition xmlns:c="sap.ui.core" xmlns:viz="sap.viz.ui5.controls" xmlns:viz.data="sap.viz.ui5.data"
xmlns:viz.feeds="sap.viz.ui5.controls.common.feeds">
<viz:VizFrame uiConfig="{applicationSet:'fiori'}" vizType='donut'>
<viz:dataset>
<viz.data:FlattenedDataset data="{manipulatedData>/}">
<viz.data:dimensions>
<viz.data:DimensionDefinition name="Gender" value="{manipulatedData>gender}"/>
</viz.data:dimensions>
<viz.data:measures>
<viz.data:MeasureDefinition name="Amount" value="{manipulatedData>amount}"/>
</viz.data:measures>
</viz.data:FlattenedDataset>
</viz:dataset>
<viz:feeds>
<viz.feeds:FeedItem uid="color" type="Dimension" values="Gender"/>
<viz.feeds:FeedItem uid="size" type="Measure" values="Amount"/>
</viz:feeds>
</viz:VizFrame>
</c:FragmentDefinition>
Main.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"sap/ui/core/Fragment"
], function (Controller, JSONModel, Fragment) {
"use strict";
return Controller.extend("demo.chart.controller.Main", {
onInit: function () {
const mDefault = this.getOwnerComponent().getModel();
const mManipulatedData = new JSONModel([{
gender: "F",
amount: 0
}, {
gender: "M",
amount: 0
}, {
gender: "X",
amount: 12
}]);
this.getView().setModel(mManipulatedData, "manipulatedData");
console.log(this.getView().getModel("manipulatedData"));
mDefault.read("/ContactSet", {
success: oData => {
const aManipulatedData = mManipulatedData.getData();
oData.results.forEach(entry => {
aManipulatedData.forEach(type => {
if (entry.Sex === type.gender) {
type.amount++
}
})
});
Fragment.load({
name: "demo.chart.view.Chart",
controller: this
}).then(oFragment => {
this.getView().addDependent(oFragment);
this.byId("page").addContent(oFragment);
});
}
})
}
});
});
Structure of the service (GWSAMPLE_BASIC/ContactSet)
{
"d": {
"results": [
{
"__metadata": {
"id": "https://sapes5.sapdevcenter.com/sap/opu/odata/IWBEP/GWSAMPLE_BASIC/ContactSet(guid'0050568c-901d-1eda-bcae-e8394de7e116')",
"uri": "https://sapes5.sapdevcenter.com/sap/opu/odata/IWBEP/GWSAMPLE_BASIC/ContactSet(guid'0050568c-901d-1eda-bcae-e8394de7e116')",
"type": "GWSAMPLE_BASIC.Contact"
},
"Address": {
"__metadata": {
"type": "GWSAMPLE_BASIC.CT_Address"
},
"City": "Walldorf",
"PostalCode": "69190",
"Street": "Robert-Koch-Straße",
"Building": "1",
"Country": "DE",
"AddressType": "02"
},
"ContactGuid": "0050568c-901d-1eda-bcae-e8394de7e116",
"BusinessPartnerID": "0100000000",
"Title": "",
"FirstName": "Karl",
"MiddleName": "",
"LastName": "Müller",
"Nickname": "",
"Initials": "",
"Sex": "M",
"PhoneNumber": "0622734567",
"FaxNumber": "0622734004",
"EmailAddress": "do.not.reply#sap.com",
"Language": "EN",
"DateOfBirth": null,
"ToBusinessPartner": {
"__deferred": {
"uri": "https://sapes5.sapdevcenter.com/sap/opu/odata/IWBEP/GWSAMPLE_BASIC/ContactSet(guid'0050568c-901d-1eda-bcae-e8394de7e116')/ToBusinessPartner"
}
}
}
]
}
}
So, as you can see, I'm only interested in the aggregated values of the Sex property. My current solution is to loop through all the entries of the entity set and increase the property in a custom JSON model. This is not only very inperformant because you do the heavy-lifting on the client side, it also requires you to know all the possible values for the data displayed in the chart right away. Is there a way to do this with OData queries or do I have to create a new entity set with the comulated data in my SAP system?
OData should be able to count and group, as described in this quesiton: OData v4 groupby with $count
But I doubt your SAP OData Service will, most of the time, those services do not implement the full OData specification.
You can improve your js by not using two nested for loops but something like this:
mGender = {};
oData.results.forEach(entry => {
if (!mGender[entry.Sex])
{mGender[entry.Sex] = 0
}
mGender[entry.Sex]++
});

Aggregation Binding: How to Put Different Control with Different Aggregation Binding inside the Control which Already Has Aggregation Binding

I have the following data in my model:
{
vertexTable: [
{
"NAME": "Tethys",
"TYPE":"titan",
"RESIDENCE":"Othrys"
},
{
"NAME": "Oceanus",
"TYPE": "titan",
"RESIDENCE": "Othrys"
}
],
vertexAttributes: [
{
"COLUMN_NAME": "NAME",
"DATA_TYPE_NAME": "VARCHAR"
},
{
"COLUMN_NAME": "TYPE",
"DATA_TYPE_NAME": "VARCHAR"
},
{
"COLUMN_NAME": "RESIDENCE",
"DATA_TYPE_NAME": "VARCHAR"
}
]
}
I want to display this data in my view such that there is a label coming from vertexAttributes/COLUMN_NAME and each label has an associated dropdown / select box coming from ..
vertexTable/NAME for the NAME label,
vertexTable/TYPE for the TYPE label, and
vertexTable/RESIDENCE for the RESIDENCE label.
I thought about using a Form control which has aggregation binding to formElements="{/vertexattributes}" and then each Form Element bound to "{COLUMN_NAME}" from vertexAttributes. But now, I want to put the Select control which has an aggregation binding to vertexTable and then the items bound to NAME, TYPE, and RESIDENCE.
Below is the code I tried in my view:
<f:form>
<f:FormContainer formElements="{/vertexAttributes}">
<f:FormElement label="{COLUMN_NAME}">
<List id="values" items="{path: 'VALUES', templateShareable: false}">
<StandardListItem title="{value}"></StandardListItem>
</List>
</f:FormElement>
</f:FormContainer>
</f:form>
I had to use templateShareable because I was getting an error. Not completely sure what it is.
Also, instead of select / dropdown, I am using a List to show the complete values.
Is this the right way to do it or is there a better way?
I came across factory function but I am unable to implement it.
Here is a working example:
sap.ui.getCore().attachInit(() => sap.ui.require([
"sap/ui/model/json/JSONModel",
"sap/ui/layout/form/FormElement",
"sap/m/Label",
"sap/m/Select",
"sap/ui/core/Item",
], (JSONModel, FormElement, Label, Select, Item) => sap.ui.xmlview({
viewContent: `<mvc:View
xmlns:mvc="sap.ui.core.mvc"
xmlns:form="sap.ui.layout.form"
xmlns="sap.m"
controllerName="demo.MyController"
>
<form:Form editable="true">
<form:layout>
<form:ResponsiveGridLayout/>
</form:layout>
<form:FormContainer
formElements="{
path: '/vertexAttributes',
factory: '.createFormElement'
}"
/>
</form:Form>
</mvc:View>`,
controller: sap.ui.controller("demo.MyController", {
createFormElement: function(id, context) {
const columnName = context.getProperty("COLUMN_NAME");
return new FormElement(id).setLabel(new Label({
text: columnName,
})).addField(new Select().bindItems({
path: "/vertexTable",
template: new Item().bindProperty("text", columnName),
}));
},
}),
}).setModel(new JSONModel({
vertexTable: [{
"NAME": "Tethys",
"TYPE": "Titan1",
"RESIDENCE": "Othrys1"
},
{
"NAME": "Oceanus",
"TYPE": "Titan2",
"RESIDENCE": "Othrys2"
},
],
vertexAttributes: [{
"COLUMN_NAME": "NAME",
"DATA_TYPE_NAME": "VARCHAR"
},
{
"COLUMN_NAME": "TYPE",
"DATA_TYPE_NAME": "VARCHAR"
},
{
"COLUMN_NAME": "RESIDENCE",
"DATA_TYPE_NAME": "VARCHAR"
},
]
})).placeAt("content")));
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" id="sap-ui-bootstrap"
data-sap-ui-libs="sap.ui.layout, sap.m, sap.ui.core"
data-sap-ui-preload="async"
data-sap-ui-theme="sap_belize"
data-sap-ui-compatVersion="edge"
data-sap-ui-resourceRoots='{"demo": "./"}'
data-sap-ui-xx-waitForTheme="true"></script>
<body id="content" class="sapUiBody sapUiSizeCompact"></body>
The design of your data structure is somewhat questionable. The fact that the value (of vertexAttributes/COLUMN_NAME) is the key (of vertexTable/*) in other objects makes the implementation complicated. Nevertheless, as you've already tried before, displaying the appropriate data is still possible with a factory function.
The example above creates a Select control in each iteration of the createFormElement call. Then, the select item can bind its text with the current columnName as a path.
About the templateShareable: https://stackoverflow.com/a/47734086/5846045

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!

Limit the number of rows of autocomplete result , and match strings with starting letters only

I'm using jquery autocomplete in my project,
<div class="ui-widget">
<label for="tags">Tags: </label>
<input id="tags">
</div>
Json file
[
{ "value": "Saree",
"url": "/collection/saree" },
{ "value": "Lehangas",
"url": "/collection/lehangas" },
{ "value": "Dresses",
"url": "/collection/dresses" },
{ "value": "Tunics",
"url": "/collection/tunics" },
{ "value": "Kurtis",
"url": "/collection/kurtis" },
{ "value": "Blouses",
"url": "/collection/blouses" },
{ "value": "Duppattas",
"url": "/collection/duppattas" },
{ "value": "Shawls",
"url": "/collection/shawls" },
{ "value": "Plazos",
"url": "/collection/plazos" },
{ "value": "Skirts",
"url": "/collection/skirts" },
{ "value": "Patiala",
"url": "/collection/patiala" }
]
my js file:
$( function (){
$( "#tags" ).autocomplete({
source: "/static/admin/json/search.json",
select: function (event, ui) {
window.location = ui.item.url;
}
});
});
It displays all the results no matter which character i enter. I want the string to be matched according to its first letter and the following letters. And also, i want to limit the number of rows displayed to 10.
So , please some one help me with this.
Thanks in advance.
HTML
<div class="ui-widget">
<label for="tags">Tags: </label>
<input id="tags">
</div>
Here I have no idea whether you are getting json data in your js file or not , So In my case json data is available in js file and I am accessing json data in my own way I hope you can manage those things.
JS File
var data = [
{ "value": "Saree",
"url": "/collection/saree" },
{ "value": "Lehangas",
"url": "/collection/lehangas" },
{ "value": "Dresses",
"url": "/collection/dresses" },
{ "value": "Tunics",
"url": "/collection/tunics" },
{ "value": "Kurtis",
"url": "/collection/kurtis" },
{ "value": "Blouses",
"url": "/collection/blouses" },
{ "value": "Duppattas",
"url": "/collection/duppattas" },
{ "value": "Shawls",
"url": "/collection/shawls" },
{ "value": "Plazos",
"url": "/collection/plazos" },
{ "value": "Skirts",
"url": "/collection/skirts" },
{ "value": "Patiala",
"url": "/collection/patiala" }
]
$( function (){
$( "#tags" ).autocomplete({
source: function(request, response){
var lengthOfSearch= request.term.length;
var arr = jQuery.map(data, function( element, index ) {
if(element.value.substr(0,lengthOfSearch).toLowerCase() === request.term.toLowerCase()){
return element;
}
});
response(arr.slice(0,10));
},
select: function (event, ui) {
window.location = ui.item.url;
}
});
});
<link href="https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<div class="ui-widget">
<label for="tags">Tags: </label>
<input id="tags">
</div>