SAPUI5 Two oData Datasources within manifest / descriptor - odata

i'm using SAPUI5 Version gt 1.30 and I try to define the automatic model instantiation of two external services within the manifest.
My first question is, are more than one odata services allowed? Sorry, but i'cant find it out by the documentation.
The default datasource ("") works. But the second datasource ("HLA") not.
If more than once are allowed, please could somebody look for the right definition.
"sap.app":
"dataSources": {
"mainService": {
"uri": "path.xsodata/",
"type": "OData",
"settings": {
"odataVersion": "2.0",
"localUri": "localService/metadata.xml"
}
}
"secondService": {
"uri": "/path.......xsodata/",
"type": "OData",
"settings": {
"annotations": [],
"odataVersion": "2.0",
"localUri": ""
}
}
},
"sap.ui5":
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "....i18n.i18n"
}
},
"": {
"dataSource": "mainService",
"settings": {
"metadataUrlParams": {
"sap-documentation": "heading"
}
}
},
"HLA": {
"dataSource": "secondService",
"settings": {
"metadataUrlParams": {
"sap-documentation": "heading"
}
}
}
}
Thanks a lot!

Not sure if you have solved your problem, but I encountered the same problem today too. And I realise you need to use different notations when binding those two data sources to a control. I'm using XML view btw.
For the default "" model:
<Table id="table" inset="true" items="{/}" >
<columns>
<Column>
<Text text="ID"/>
</Column>
<Column>
<Text text="Name"/>
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{id}"/>
<Text text="{name}"/>
</cells>
</ColumnListItem>
</items>
</Table>
For the second model named "Users":
<Table id="userTable" inset="true" items="{Users>/}">
<columns>
<Column>
<Text text="ID"/>
</Column>
<Column>
<Text text="Last Name"/>
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{Users>empNumber}"/>
<Text text="{Users>lastName}"/>
</cells>
</ColumnListItem>
</items>
</Table>
It works for me :)

Of course you can have more than one data source, but it is recommended by the fiori guidelines to only use one.
Do you want your address to look like this path.......xsodata/ ?
Also I think you should remove the localUri from the second data source.
Because if there is no metadata document this might throw an arror.
"secondService": {
"uri": "/proxy/service.xsodata",
"type": "oData",
"settings": {
"odataVersion": "2.0"
}
}
What is the output of the debugger? Do you get an error?
Also your oData model should have a type.
"HLA": {
"dataSource": "secondService",
"type": "sap.ui.model.odata.v2.ODataModel"
}

Related

SAPUI5 XmlView Show Data from Aggregation Binding Child

I need to get all the ContactEmployees contacts of each BusinessPartners client, in a Table List. Contacts are loaded but I can not show fields
My request https://***/BusinessPartners?$select=ContactEmployees&$skip=0&$top=20
My JSON Result from request "Only Selected ContactEmployees"
{
"#odata.context" : "https://**********$metadata#BusinessPartners",
"value" : [
{
"ContactEmployees" : [
{
"CardCode" : "C000002",
"Name" : "Jose Duran",
"Position" : null,
"Address" : null,
"Phone1" : null,
"Phone2" : null
},
{
"CardCode" : "C000003",
"Name" : "Leo Manuel",
"Position" : null,
"Address" : null,
"Phone1" : null,
"Phone2" : null
}
]
},
{
"ContactEmployees" : [
{
"CardCode" : "C000010",
"Name" : "MILDRED MEJIA",
"Position" : null,
"Address" : null,
"Phone1" : null,
"Phone2" : null
}
]
},
{
"ContactEmployees" : []
},
{
"ContactEmployees" : []
}
],
"#odata.nextLink" : "BusinessPartners?$select=ContactEmployees&$skip=23&$top=5"
}
My Table View
<Table id="idPartnerTable"
growing="true"
growingScrollToLoad="true"
inset="false"
items="{
path: '/BusinessPartners',
parameters:{
$select:'ContactEmployees,CardCode'
},
sorter: {
path: 'CardCode'
}
}">
<columns>
<Column id="carcode">
<Text text="ID" />
</Column>
<Column id="nombre" >
<Text text="Name" />
</Column>
</columns>
<items>
<ColumnListItem
type="Navigation"
press="onPress">
<cells>
<Text text="{ContactEmployees/CardCode} "/>
<Text text="{ContactEmployees/Name} "/>
</cells>
</ColumnListItem>
</items>
</Table>
CONSOLE ERRORS
Failed to drill-down into ('BUSINESSPARTNERS
CODES')/ContactEmployees/Name, invalid segment: Name -
/******/BusinessPartners?$select=ContactEmployees,CardCode&$orderby=CardCode sap.ui.model.odata.v4.lib._Cache
If I change {ContactEmployees/Name} to {ContactEmployees/0/Name} they are shown but only 1 single contact, and I need to bring them all
Use the expand option in order to get the ContactEmployees of each BusinessPartners.
try this :
<Table id="idPartnerTable"
growing="true"
growingScrollToLoad="true"
inset="false"
items="{
path: '/BusinessPartners',
parameters:{
expand:'ContactEmployees'
},
sorter: {
path: 'CardCode'
}
}">
here is plunker it could help you : http://plnkr.co/edit/Q6KqSRk4kk0V81AdCrvQ?p=preview

UI5 using something like .. in bindingpath

I using a oData-Model that returns for .../User a list like that:
[
{
Id: 5
Name: "name",
Desc: "desc",
IsEditable: false,
Items: [
{ itemName: "Name1" },
{ itemName: "Name2" },
{ itemName: "Name3" },
{ itemName: "Name4" }
]
},
...
]
I bind this to my xml view like that:
<View binding="model>/User(5)">
<Text text="model>Name" />
<Text text="model>Desc" />
<List items="{model>Items}" >
<CustomListItem>
<input text="{model>itemName}" editable="{model>../IsEditable}" />
</CustomListItem>
</List>
</View>
So now i would like to disable the input by its parent "IsEditable" proppertie.
How i can make this happen?
To achieve this you have to edit your code like this:
(Relative binding in JSON Models without a "/", absolute with a "/")
<List items="{model>/Items}" >
<CustomListItem>
<Input value="{model>itemName}" editable="{model>/IsEditable}" />
</CustomListItem>

How to search for all nodes in a sap.m.Tree?

I'm currently working at a MasterDetail application for my company, which provides expandable categorys represented as nodes.
The binding of the nodes and it's child nodes with a navigation property isn't a problem. However, if I want to search for a certain group node in the search field above, it only filters between the four highest nodes. It can search for the nodes on the first level, but it isn't able to find nodes if they're below the first level.
Binding of the tree:
<Tree
selectionChange="onSelectionChange"
id="list"
noDataText="{masterView>/noDataText}"
busyIndicatorDelay="{masterView>/delay}"
items="{path: '/GroupNodes',
parameters : {
expand: 'ChildGroupNodes',
navigation: {
'GroupNodes': 'ChildGroupNodes'
}
}
}">
<StandardTreeItem
title="{Stext}"
type="Navigation"
press="onSelectionChange"/>
</Tree>
onSearch:
onSearch: function(oEvent) {
if (oEvent.getParameters().refreshButtonPressed) {
this.onRefresh();
return;
}
var sQuery = oEvent.getParameter("query");
if (sQuery) {
this._oListFilterState.aSearch = [new Filter("Stext", FilterOperator.Contains, sQuery)];
} else {
this._oListFilterState.aSearch = [];
}
this._applyFilterSearch();
},
_applyFilterSearch:
_applyFilterSearch: function() {
var aFilters = this._oListFilterState.aSearch.concat(this._oListFilterState.aFilter),
oViewModel = this.getModel();
this._oList.getBinding("items").filter(aFilters, "Application");
if (aFilters.length !== 0) {
oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataWithFilterOrSearchText"));
} else if (this._oListFilterState.aSearch.length > 0) {
oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataText"));
}
},
Filterstate in the onInit() function:
this._oListFilterState = {
aFilter: [],
aSearch: []
};
Metadata:
<EntityType Name="GroupNode" sap:content-version="1">
<Key>
<PropertyRef Name="Grpid"/>
</Key>
<Property Name="Grpid" Type="Edm.String" Nullable="false" MaxLength="8" sap:unicode="false" sap:label="Id Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
<Property Name="Short" Type="Edm.String" MaxLength="12" sap:unicode="false" sap:label="Kürzel Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Stext" Type="Edm.String" MaxLength="40" sap:unicode="false" sap:label="Bezeichnung Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
<Property Name="Begda" Type="Edm.DateTime" Precision="0" sap:unicode="false" sap:label="Beginndatum" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Endda" Type="Edm.DateTime" Precision="0" sap:unicode="false" sap:label="Endedatum" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Level" Type="Edm.Int32" sap:unicode="false" sap:label="Level" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Parentid" Type="Edm.String" Nullable="false" MaxLength="8" sap:unicode="false" sap:label="ParentId" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
<NavigationProperty Name="ChildGroupNodes" Relationship="Z_HR_LSO_WORKCENTER_SRV.GroupNodeToParent" FromRole="FromRole_GroupNodeToParent" ToRole="ToRole_GroupNodeToParent"/>
<NavigationProperty Name="GroupToTrainingType" Relationship="Z_HR_LSO_WORKCENTER_SRV.GroupToTrainingType" FromRole="FromRole_GroupToTrainingType" ToRole="ToRole_GroupToTrainingType"/>
</EntityType>
We're working with OData V2, so there's no possibility to implement an FilterContains.All filter.
Is it even possible to filter through the child nodes of a sap.m.Tree in the front-end?
First of all, building the tree hierarchy via navigation is deprecated since 1.44. Instead, SAP recommends to leverage metadata annotations:
The use of navigation properties to build up the hierarchy structure is deprecated and it is recommended to use the hierarchy annotations [...].
Once the migration to the annotation approach is done, filter either client-side or server-side.
Client-side
The operationMode of the ODataTreeBinding (or the defaultOperationMode of the v2.ODataModel) needs to be "Client".
When calling .filter, the FilterType as the 2nd argument needs to be "Control".
Here is a working example: https://embed.plnkr.co/moTGOT
Server-side
For server-side filtering, only the FilterType "Application" and the operationMode: "Server" are supported. In that case, the server needs to respond to the $filter request with a ready-made tree structure. The same applies to paging requests for sibling and child nodes.
Limitations
suspended: true in the binding info is not yet supported (Issue #3161).
The above content applies to OData V2 only.
V4 ODataModel doesn't support tree binding at all yet (Issue #2728).
I think this may have something to do with the way you're binding the items, because I am able to filter on child nodes just fine in my example using your JS.
Check that this._oList.getItems() has all the items in it before you do the filter.
I'll post my code, so you can rebuild my project and get a feel for how it works. Let me know if you have more questions.
Page.view.xml
<mvc:View
controllerName="sap.m.sample.Tree.Page"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<SearchField value="{json>/search}" search="onSearch"/>
<Tree
id="Tree"
items="{path: '/'}">
<StandardTreeItem title="{text}"/>
</Tree>
</mvc:View>
Page.controller.js
sap.ui.define(['sap/ui/core/mvc/Controller', 'sap/ui/model/json/JSONModel', 'sap/ui/model/Filter', 'sap/ui/model/FilterOperator'],
function(Controller, JSONModel, Filter, FilterOperator) {
"use strict";
var PageController = Controller.extend("sap.m.sample.Tree.Page", {
onInit: function(evt) {
// set explored app's demo model on this sample
var oModel = new JSONModel(jQuery.sap.getModulePath("sap.m.sample.Tree", "/Tree.json"));
this.getView().setModel(oModel);
var oJSONModel = new JSONModel();
this.getView().setModel("json", oJSONModel);
this._oList = this.byId("Tree");
this._oListFilterState = {
aFilter: [],
aSearch: []
};
},
onSearch: function(oEvent) {
if (oEvent.getParameters().refreshButtonPressed) {
this.onRefresh();
return;
}
var sQuery = oEvent.getParameter("query");
if (sQuery) {
this._oListFilterState.aSearch = [new Filter("text", FilterOperator.Contains, sQuery)];
} else {
this._oListFilterState.aSearch = [];
}
this._applyFilterSearch();
},
_applyFilterSearch: function() {
var aFilters = this._oListFilterState.aSearch.concat(this._oListFilterState.aFilter);
//oViewModel = this.getModel();
this._oList.getBinding("items").filter(aFilters, "Application");
// if (aFilters.length !== 0) {
// oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataWithFilterOrSearchText"));
// } else if (this._oListFilterState.aSearch.length > 0) {
// oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataText"));
// }
}
});
return PageController;
});
Tree.json
[
{
"text": "Node1",
"ref": "sap-icon://attachment-audio",
"nodes":
[
{
"text": "Node1-1",
"ref": "sap-icon://attachment-e-pub",
"nodes":[
{
"text": "Node1-1-1",
"ref": "sap-icon://attachment-html"
},
{
"text": "Node1-1-2",
"ref": "sap-icon://attachment-photo",
"nodes":[
{
"text": "Node1-1-2-1",
"ref": "sap-icon://attachment-text-file",
"nodes":[
{
"text": "Node1-1-2-1-1",
"ref": "sap-icon://attachment-video"
},
{
"text": "Node1-1-2-1-2",
"ref": "sap-icon://attachment-zip-file"
},
{
"text": "Node1-1-2-1-3",
"ref": "sap-icon://course-program"
}
]
}
]
}
]
},
{
"text": "Node1-2",
"ref": "sap-icon://create"
}
]
},
{
"text": "Node2",
"ref": "sap-icon://customer-financial-fact-sheet"
}
]
That's all you should need, but just incase you need these as well...
index.html
<!DOCTYPE HTML>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<title>Tree - Basic</title>
<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_belize"
data-sap-ui-xx-bindingSyntax="complex"
data-sap-ui-preload="async"
data-sap-ui-compatVersion="edge"
data-sap-ui-resourceroots='{"sap.m.sample.Tree": "./", "sap.ui.demo.mock": "mockdata"}'>
</script>
<!-- Application launch configuration -->
<script>
sap.ui.getCore().attachInit(function() {
new sap.m.App ({
pages: [
new sap.m.Page({
title: "Tree - Basic",
enableScrolling : true,
content: [ new sap.ui.core.ComponentContainer({
name : "sap.m.sample.Tree"
})]
})
]
}).placeAt("content");
});
</script>
</head>
<!-- UI Content -->
<body class="sapUiBody" id="content" role="application">
</body>
Component.js
sap.ui.define(['sap/ui/core/UIComponent'],
function(UIComponent) {
"use strict";
var Component = UIComponent.extend("sap.m.sample.Tree.Component", {
metadata : {
rootView : "sap.m.sample.Tree.Page",
dependencies : {
libs : [
"sap.m",
"sap.ui.layout"
]
},
config : {
sample : {
files : [
"Page.view.xml",
"Page.controller.js",
"Tree.json"
]
}
}
}
});
return Component;
});
Not sure if you managed to solve your problem, but I actually did this exact thing a few months ago.
In your controller, put the following function:
onSearch: function(oEvent) {
var searchVal = oEvent.getParameter("newValue");
var treeFilter = new sap.ui.model.Filter("Text", sap.ui.model.FilterOperator.Contains, searchVal);
var oBinding = this.getView().byId("tree").mBindingInfos.rows.binding;
oBinding.filter(treeFilter, FilterType.Application);
oBinding.expandToLevel(3);
},
And this should work straight away. Whatever you enter in the search box, it will populate the tree (up to 3 levels, but this can be changed) with your filter.
The onSearch function is of course executed on the livechange of the search field.

SAPUI5 table binditems from expand performed in a bindelement

I am trying a proof of concept in SAPui5 to check the content of a searchfield, bind a result into another field, if i don't have any result i display an error message that has been written in a message field of my odata
so far i succeeded that this way
this.getView().bindElement({
path: "/ExcSet('" + evt.getSource().getValue() + "')",
model: "EXCEPTION",
events: {
dataReceived: function(response) {
if (response.mParameters.data.Message !== '') {
MessageBox.error(response.mParameters.data.Message);
}
}
}
(If someone knows how to do that a better way, because using mParameters is not the best way, it is welcome)
Now, I want to extend my odata call with an expand navigation to display a table of results, by not using another odata call that the one i used already, so here is my code so far :
eanSearch: function(evt) {
var oView = this.getView();
var oTemplate = new ColumnListItem({
cells: [
new Text({
text: "{Volum}"
}),
new Text({
text: "{Voleh}"
})
]
});
this.getView().bindElement({
path: "/ExcSet('" + evt.getSource().getValue() + "')",
model: "EXCEPTION",
parameters: {
expand: "ExcMarmNav"
},
events: {
dataReceived: function(response) {
if (response.mParameters.data.Message !== '') {
MessageBox.error(response.mParameters.data.Message);
}
}
}
});
oView.byId("table").bindItems({
path : '/ExcMarmNav',
template : oTemplate
});
}
The data of the expand is loaded into my response as you can see here
data: {
"ExcSet('5410366897766')": {
"__metadata": {
"id": "http://...:8000/sap/opu/odata/sap/ZEXCEPTION_SRV/ExcSet('5410366897766')",
"uri": "http://...:8000/sap/opu/odata/sap/ZEXCEPTION_SRV/ExcSet('5410366897766')",
"type": "ZEXCEPTION_SRV.Exc"
},
"Matnr": "000000000040000000",
"Ean": "5410366897766",
"Message": "",
"ExcMarmNav": {
"__list": [
"MarmSet(Matnr='40000000',Meinh='EA')"
]
}
},
"MarmSet(Matnr='40000000',Meinh='EA')": {
"__metadata": {
"id": "http://...:8000/sap/opu/odata/sap/ZEXCEPTION_SRV/MarmSet(Matnr='40000000',Meinh='EA')",
"uri": "http://...:8000/sap/opu/odata/sap/ZEXCEPTION_SRV/MarmSet(Matnr='40000000',Meinh='EA')",
"type": "ZEXCEPTION_SRV.Marm"
},
"Matnr": "40000000",
"Meinh": "EA",
"Umrez": "1",
"Umren": "1",
"Eannr": "",
"Ean11": "5410366897766",
"Numtp": "HE",
"Laeng": "20.000",
"Breit": "20.000",
"Hoehe": "10.000",
"Meabm": "CM",
"Volum": "4000.000",
"Voleh": "CCM",
"Brgew": "2.500",
"Gewei": "KG",
"Mesub": "",
"Atinn": "0000000000",
"Mesrt": "00",
"Xfhdw": "",
"Xbeww": "",
"Kzwso": "",
"Msehi": "",
"BflmeMarm": "",
"GtinVariant": "",
"NestFtr": "0",
"MaxStack": 0,
"Capause": "0.000",
"Ty2tq": ""
}
}
But i don't know how to use resultset to bind it into a table, my code above is not working, if someone has an idea of the params to use for my binditems, or if there is another way to do it?
Best regards
Denis
I managed to do the binding of my table using an intermediate JSON Model based on the response of the Odata Model
eanSearch: function(evt) {
var oView = this.getView();
this.getView().bindElement({
path: "/ExcSet('" + evt.getSource().getValue() + "')",
model: "EXCEPTION",
parameters: {
expand: "ExcMarmNav"
},
events: {
dataReceived: function(response) {
if (response.mParameters.data.Message !== '') {
MessageBox.error(response.mParameters.data.Message);
} else {
var model = new JSONModel({
"items": response.mParameters.data.ExcMarmNav
});
oView.setModel(model, "itemModel");
}
}
}
});
And this is the XML view:
<Table id="table" items="{itemModel>/items}">
<columns>
<Column><Label/></Column>
<Column><Label/></Column>
</columns>
<ColumnListItem>
<cells>
<Text text="{itemModel>Volum}"/>
<Text text="{itemModel>Voleh}"/>
</cells>
</ColumnListItem>
</Table>
works fine but I would like to avoid using the intermediate JSON Model and bind directly the array of the response in the table
Ok I managed to find what I needed :)
so the controller:
eanSearch: function(evt) {
var oView = this.getView();
this.getView().bindElement({
path: "/ExcSet('" + evt.getSource().getValue() + "')",
model: "EXCEPTION",
parameters: {
expand: "ExcMarmNav"
},
events: {
dataReceived: function(response) {
if (response.mParameters.data.Message !== '') {
MessageBox.error(response.mParameters.data.Message);
}
}
}
});
}
and the view:
<Table noDataText="No Data" items="{EXCEPTION>ExcMarmNav}">
<columns>
<Column><Label/></Column>
<Column><Label/></Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{EXCEPTION>Volum}"/>
<Text text="{EXCEPTION>Voleh}"/>
</cells>
</ColumnListItem>
</items>
</Table>

How to customise Shell container in SAPUI5

I've a shell container and on big screens i want to utilize full with of screen. i want to cover full area. how i can customize it.
I assume you are using XML for your views. Add the following attribute appWidthLimited="false" to the Shell tag.
When working with a manifest.json file and the UI5-framework instantiates a shell control, do the following (appWidthLimited="false" cannot be used as you don't have a xml containing a shell 'tag').
manifest.json
...
"sap.ui5": {
"config": {
"fullWidth": true
},
...
...
As per latest documentation, I referred to 1.48.X, and it's not there in the sap.ui5 anymore:
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "sap-icon://add-contact",
"favIcon": "icon/F1373_Approve_Purchase_Orders.ico",
"phone": "icon/launchicon/57_iPhone_Desktop_Launch.png",
"phone#2": "icon/launchicon/114_iPhone-Retina_Web_Clip.png",
"tablet": "icon/launchicon/72_iPad_Desktop_Launch.png",
"tablet#2": "icon/launchicon/144_iPad_Retina_Web_Clip.png"
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": false
},
"supportedThemes": [
"sap_hcb"
],
"fullWidth": true
},
For more info: https://openui5.hana.ondemand.com/#/topic/be0cf40f61184b358b5faedaec98b2da
Also, if you are using sap.m.Shell, then the above will not help.
For that you need to set the property appWidthLimited: false:
<script>
sap.ui.getCore().attachInit(function () {
new sap.m.Shell({
app: new sap.ui.core.ComponentContainer({
height: "100%",
name: "APPNAME"
}),
appWidthLimited: false
})
.placeAt("content");
});
</script>
It can be done either statically, via XML-template:
<mvc:View controllerName="letterboxing.widescreen.controller.index" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m">
<Shell id="shell" appWidthLimited="false">
<App id="app">
<pages>
<Page id="page" title="{i18n>title}">
<content></content>
</Page>
</pages>
</App>
</Shell>
</mvc:View>
Or dynamically via JS-controller, which will set appWidthLimited:false to the sap.m.Shell.

Resources