Binding OData Service to view - sapui5

I want to bind oData services to SAPUI5 view but not bind. How to fix this problem?
tes.view.xml
<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
controllerName="tes.tes">
<Page title="Title">
<content>
<Label text="Hai dunia!"></Label>
<List
headerText="Products"
items="{
path: '/DATA'
}" >
<StandardListItem
title="{NAME}"
counter="{DESC}"/>
</List>
</content>
</Page>
</mvc:View>
tes.controller.js
sap.ui.define([
'jquery.sap.global',
'sap/m/MessageToast',
'sap/ui/core/Fragment',
'sap/ui/core/mvc/Controller',
'sap/ui/model/json/JSONModel'
], function(jQuery, MessageToast, Fragment, Controller, JSONModel) {
"use strict";
var CController = Controller.extend("tes.tes", {
onInit : function () {
var model = new sap.ui.model.odata.ODataModel("http://192.168.78.23:8000/Tes/WebContent/tes/data.xsodata", false);
console.log(model);
var model2 = new JSONModel(model);
this.getView().setModel(model2);
}
});
return CController;
});
data.xsodata
service namespace "tes" {
"HANATES"."USER" as "DATA";
}
project structure:
data:
Data not bind to view SAPUI5.
Thanks.
Bobby

The problem lies with your models:
var model = new sap.ui.model.odata.ODataModel("http://192.168.78.23:8000/Tes/WebContent/tes/data.xsodata", false); - This step is right. You are creating a oData Model.
var model2 = new JSONModel(model); - This here is a problem. JSONModel constructor will accept either the URL where to load the JSON from or a JS object but you are passing an OData Model instance. This step will not fetch the data from oDataModel.
this.getView().setModel(model2);' - Change this to this.getView().setModel(model);` - Make ODataModel as your default model to view ( Since you have done the binding - /data in your view.)
NOTE: If you want to bind a JSONModel to your view, then :
Call ODataModel.read method to fetch data from Server.
in Sucess handler of oDataModel.read, copy the data to JSON Model.
Bind the respective JSON Model to view.
LINK: for OdataModel read method https://openui5.hana.ondemand.com/#docs/api/symbols/sap.ui.model.odata.ODataModel.html#read
Let me know if you need more info.

Related

SAPUI5 sap.m.list in a popover is emtpy when populated using bindElement

I have a table (sap.m.table) with different columns. One columns contains a link and when the user clicks on the link a popover fragment opens and shows some details in a list (sap.m.list). The data is coming from an oData Service. One Entity feeds the table and through a navigation property the data for the popover is fetched.
I have a working example for this scenario where I create the the list template in the controller. But I believe that this should also be possible just by xml and a bindElement in the controller. What is the mistake in my second scenario?
First Scenario (which is working fine):
Popover XML:
<Popover
showHeader="false"
contentWidth="320px"
contentHeight="300px"
placement="Bottom"
ariaLabelledBy="master-title">
<Page
id="master"
class="sapUiResponsivePadding--header"
title="Aktionen">
<List
id="AktionList">
</List>
</Page>
</Popover>
Calling the Popover in the controller file (sPath is /TableEntity('123456')/AktionSet):
if (!this._oPopover) {
Fragment.load({
id: "popoverNavCon",
name: "bernmobil.ZPM_STOERUNG_FDA.view.AktionPopover",
controller: this
}).then(function(oPopover){
this._oPopover = oPopover;
this.getView().addDependent(this._oPopover);
var oList = Fragment.byId("popoverNavCon", "AktionList");
var oItemTemplate = this._BuildItemTemplate();
oList.bindAggregation("items", sPath, oItemTemplate);
this._oPopover.openBy(oControl);
}.bind(this));
} else {
var oList = Fragment.byId("popoverNavCon", "AktionList");
var oItemTemplate = this._BuildItemTemplate();
oList.bindAggregation("items", sPath, oItemTemplate);
this._oPopover.openBy(oControl);
}
_BuildItemTemplate: function(){
var oItemTemplate = new sap.m.ObjectListItem({
title:"{AktionsBez}",
type: "Inactive"
});
oItemTemplate.addAttribute(new sap.m.ObjectAttribute({
text : "{Aktionstext}"
}));
oItemTemplate.addAttribute(new sap.m.ObjectAttribute({
text : "{path: 'ChangedAt', type: 'sap.ui.model.type.DateTime'}"
}));
return oItemTemplate;
}
And this is the idea of the second scenario which is calling the oDataService but not displaying any data:
Instead of having only the List definition in XML also the ObjectListItem is defined in the XML:
<List
id="AktionList"
items="{AktionSet}">
<ObjectListItem
title="{AktionsBez}"
type="Active">
<ObjectAttribute text="{Aktionstext}" />
<ObjectAttribute text="{ChangedAt}" />
</ObjectListItem>
</List>
And in the controller instead of building the template and doing the bindAggretation there is just:
var oList = Fragment.byId("popoverNavCon", "AktionList");
oList.bindElement(sPath);
How do I get the second scenario displaying data in the list?
I would suggest a third solution:
When pressing on your table row, get the binding context of the current row using oContext = oClickedRow.getBindingContext() or something similar. This context should point to /TableEntity('123456').
Apply this context to your Popover using oPopover.setBindingContext(oContext).
Now your Popover has the same context as your table row. The XML should look like in your second scenario.
Using the path is an extra step imo. I think it also makes more sense to bind the complete Popover and not just the List. Has the neat side effect that you can use a property of your TableEntity as the title for the Popover. It also completely avoids working with controls directly (which is one of the seven deadly sins) and you should be able to remove all those IDs.
Here is how the relevant part of the view should look:
<List
id="AktionList"
items="{AktionSet}">
<dependents>
<ObjectListItem
id="oObjectListItem"
title="{AktionsBez}"
type="Active">
<ObjectAttribute text="{Aktionstext}" />
<ObjectAttribute text="{ChangedAt}" />
</ObjectListItem>
</dependents>
</List>
And in the controller:
var oList = sap.ui.core.Fragment.byId(this.getView().createId("popoverNavCon"), "AktionList");
oList.bindItems({
path: sPath,
template: sap.ui.core.Fragment.byId(this.getView().createId("popoverNavCon"), "oObjectListItem")
});
Check dependents in sap.m.List aggregation section for details.

How to properly use JSONModel and setModel?

I'm trying to create an example screen using SAP Web IDE where clicking different buttons changes different texts around the screen.
I have a few functions at the App.controller.js and the code is this (All the functions do the same for now but affect different text areas):
onPressButton2: function () {
var oData = {
text: {
line1: "line1",
line2: "line2",
line3: "line3",
line4: "line4"
}
};
var oModel = new JSONModel(oData);
this.getView().setModel(oModel);
},
And this is corresponding part at the XML:
<items>
<Text xmlns="sap.m" text="{/text/line1}" id="text1"/>
<Text xmlns="sap.m" text="{/text/line2}" id="text2"/>
<Text xmlns="sap.m" text="{/text/line3}" id="text3"/>
<Text xmlns="sap.m" text="{/text/line4}" id="text4"/>
</items>
This works, but when I try and change different areas of the screen, the previous changes I made by clicking the buttons disappear. I assume this is because I use setModel anew every time which overwrites it but I cannot find the proper usage.
Should I create a different js file for every section in the screen?
Is there a way to update the model instead of overwriting it all?
Try to declare your JSONModel outside of the onPressButton function. You can declare it in the manifest to be visible for the entire application (controllers and views):
"sap.ui5": {
"_version": "1.1.0",
...
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
},
"MyModel" : {
"type" : "sap.ui.model.json.JSONModel"
}
Once the model is available you can set the data to it outside of the onPressButton2 function:
this.getOwnerComponent().getModel("MyModel").setData(oData)
Now, in the onPressButton2 function, you can just update the model's data using the setProperty method:
this.getOwnerComponent().getModel("MyModel").setProperty("/text/line1", "NewValue");
I think what you are searching are named models. with named models you are able create different models without overwriting them, if you want to additionally add a new model.
var oModel = new JSONModel(oData);
this.getView().setModel(oModel, "model1");
have a look at the second parameter in the setmodel method. now you can access them in the view with
<items>
<Text xmlns="sap.m" text="{model1>/text/line1}" id="text1"/>
<Text xmlns="sap.m" text="{model1>/text/line2}" id="text2"/>
<Text xmlns="sap.m" text="{model1>/text/line3}" id="text3"/>
<Text xmlns="sap.m" text="{model1>/text/line4}" id="text4"/>
</items>
You should create your model during the intilisation phase of the page lifecycle.
So, in your instance, create the model and the intial values in the onInit function of the relevant view/page:
onInit: function () {
var oData = {
text: {
line1: "line1",
line2: "line2",
line3: "line3",
line4: "line4"
}
};
var oModel = new JSONModel(oData);
this.getView().setModel(oModel);
Then, when you need to assign different values to that model for the existing values you would simply set the relevant property in the model as follows:
this.getView().getModel().setProperty("/text/line1", "<new value>");
if you wish to add an additional line you could simply get the existing model values and add the new value:
var mydata = this.getView().getModel().getProperty("/");
mydata.text["line5"] = "line5";
this.getView().setProperty("/", mydata);
Hope that helps.
I trust you are aware of the differences between the un-named model you were using and the concept of a named model.

onCloseDialog event not working in my Controller. What's wrong with my code?

I'm trying to learn SAPUI5 and following the walkthrough in SAPUI5 documentation. I'm currently in Step 17: Fragment Callbacks. I am not able to make the onCloseDialog event work. The code I double and triple checked and I could not find anything wrong. There is also no error in Chrome's console. Any insights?
Link to the guide I'm following:
https://sapui5.hana.ondemand.com/#/topic/354f98ed2b514ba9960556333428d35e
My code for:
HelloDialog.fragment.xml
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">
<Dialog id="helloDialog" title="Hello {/recipient/name}">
<content>
<core:Icon src="sap-icon://hello-world" size="8rem" class="sapUiMediumMargin"/>
</content>
<beginButton>
<Button text="{i18n>dialogCloseButtonText}" press="onCloseDialog"/>
</beginButton>
</Dialog>
My code for:
HelloPanel.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageToast"
], function(Controller, MessageToast) {
"use strict";
return Controller.extend("sap.ui.demo.wt.controller.HelloPanel", {
onShowHello: function() {
// read msg from i18n model
var oBundle = this.getView().getModel("i18n").getResourceBundle();
var sRecipient = this.getView().getModel().getProperty("/recipient/name");
var sMsg = oBundle.getText("helloMsg", [sRecipient]);
// show message
MessageToast.show(sMsg);
},
onOpenDialog: function() {
var oView = this.getView();
var oDialog = oView.byId("helloDialog");
// create dialog lazily
if (!oDialog) {
// create dialog via fragment factory
oDialog = sap.ui.xmlfragment(oView.getId(), "sap.ui.demo.wt.view.HelloDialog");
oView.addDependent(oDialog);
}
oDialog.open();
},
onCloseDialog: function() {
this.getView().byId("helloDialog").close();
}
});
});
You missed to pass the controller reference when creating the dialog from the fragment.
In case of the Walkthrough step, it's the same controller object as the dialog caller, so this should be passed:
sap.ui.xmlfragment(oView.getId(), "sap.ui.demo.wt.view.HelloDialog", this);⚠️
API reference: sap.ui.xmlfragment⚠️
⚠️ sap.ui.*fragment is deprecated!
Instead, use one of the APIs mentioned in https://stackoverflow.com/a/64541325/5846045.
Documentation topic: Instantiation of Fragments

How get selected item from StandardListItem

I'm building a master-detail app. I'm using sap.m.StandardListItem for listing master objects. I want that selected master object appear on a detail page.
<List id="lstRequest" headerText="Custom Content" selected="true" items="{
path: '/Requests',
parameters: {
expand: 'RequestTypeDetails'
}
}">
<StandardListItem
title="{RequestTypeDetails/RequestType2} - {RequestCode}"
description="{TotalAdvance}"
icon="sap-icon://request"
iconDensityAware="false"
iconInset="false"
type="Navigation"
press="onSelectApprovation"
/>
</List>
I'm following the guide from here but it doesn't work in my case.
var source = event.getSource();
var bindingobject = event.getBindingContext("Requests");
bindingobject is undefined.
Inside onSelectApprovation do the following:
var oItem = oEvent.getParameter("listItem") || oEvent.getSource());
var oCtx = oItem.getBindingContext();
var requestCode = oCtx.getProperty("RequestCode");
By the way: sap.m.List does not have a property called "selected" of type boolean. However, it has a select event that could also be used instead of using the press event of the StandardListItem...
Give the logical name while setting json model to list like below.
this.getView().byId("lstRequest").setModel(oListJson,"List");
oListJson will be your array of data.
now use your code for accessing list object in your onSelectApprovation fuction as below
onSelectApprovation : function(oEvent){
var bindingobject = oEvent.getSource().getBindingContext("List");
}

Passing Data from Master to Detail Page

I watched some tutorials about navigation + passing data between views, but it doesn't work in my case.
My goal is to achieve the follwing:
On the MainPage the user can see a table with products (JSON file). (Works fine!)
After pressing the "Details" button, the Details Page ("Form") is shown with all information about the selection.
The navigation works perfectly and the Detail page is showing up, however the data binding doesnt seem to work (no data is displayed)
My idea is to pass the JSON String to the Detail Page. How can I achieve that? Or is there a more elegant way?
Here is the code so far:
MainView Controller
sap.ui.controller("my.zodb_demo.MainView", {
onInit: function() {
var oModel = new sap.ui.model.json.JSONModel("zodb_demo/model/products.json");
var mainTable = this.getView().byId("productsTable");
this.getView().setModel(oModel);
mainTable.setModel(oModel);
mainTable.bindItems("/ProductCollection", new sap.m.ColumnListItem({
cells: [new sap.m.Text({
text: "{Name}"
}), new sap.m.Text({
text: "{SupplierName}"
}), new sap.m.Text({
text: "{Price}"
})]
}));
},
onDetailsPressed: function(oEvent) {
var oTable = this.getView().byId("productsTable");
var contexts = oTable.getSelectedContexts();
var items = contexts.map(function(c) {
return c.getObject();
});
var app = sap.ui.getCore().byId("mainApp");
var page = app.getPage("DetailsForm");
//Just to check if the selected JSON String is correct
alert(JSON.stringify(items));
//Navigation to the Detail Form
app.to(page, "flip");
}
});
Detail Form View:
<mvc:View xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:f="sap.ui.layout.form" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc" controllerName="my.zodb_demo.DetailsForm">
<Page title="Details" showNavButton="true" navButtonPress="goBack">
<content>
<f:Form id="FormMain" minWidth="1024" maxContainerCols="2" editable="false" class="isReadonly">
<f:title>
<core:Title text="Information" />
</f:title>
<f:layout>
<f:ResponsiveGridLayout labelSpanL="3" labelSpanM="3" emptySpanL="4" emptySpanM="4" columnsL="1" columnsM="1" />
</f:layout>
<f:formContainers>
<f:FormContainer>
<f:formElements>
<f:FormElement label="Supplier Name">
<f:fields>
<Text text="{SupplierName}" id="nameText" />
</f:fields>
</f:FormElement>
<f:FormElement label="Product">
<f:fields>
<Text text="{Name}" />
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
</f:formContainers>
</f:Form>
</content>
</Page>
</mvc:View>
Detail Form Controller:
sap.ui.controller("my.zodb_demo.DetailsForm", {
goBack: function() {
var app = sap.ui.getCore().byId("mainApp");
app.back();
}
});
The recommended way to pass data between controllers is to use the EventBus
sap.ui.getCore().getEventBus();
You define a channel and event between the controllers. On your DetailController you subscribe to an event like this:
onInit : function() {
var eventBus = sap.ui.getCore().getEventBus();
// 1. ChannelName, 2. EventName, 3. Function to be executed, 4. Listener
eventBus.subscribe("MainDetailChannel", "onNavigateEvent", this.onDataReceived, this);)
},
onDataReceived : function(channel, event, data) {
// do something with the data (bind to model)
console.log(JSON.stringify(data));
}
And on your MainController you publish the Event:
...
//Navigation to the Detail Form
app.to(page,"flip");
var eventBus = sap.ui.getCore().getEventBus();
// 1. ChannelName, 2. EventName, 3. the data
eventBus.publish("MainDetailChannel", "onNavigateEvent", { foo : "bar" });
...
See the documentation here: https://openui5.hana.ondemand.com/docs/api/symbols/sap.ui.core.EventBus.html#subscribe
And a more detailed example:
http://scn.sap.com/community/developer-center/front-end/blog/2015/10/25/openui5-sapui5-communication-between-controllers--using-publish-and-subscribe-from-eventbus
Even though this question is old, the scenario is still valid today (it's a typical master-detail / n-to-1 scenario). On the other hand, the currently accepted solution is not only outdated but also a result of an xy-problem.
is there a more elegant way?
Absolutely. Take a look at this Flexible Column Layout tutorial: https://sdk.openui5.org/topic/c4de2df385174e58a689d9847c7553bd
No matter what control is used (App, SplitApp, or FlexibleColumnLayout), the concept is the same:
User clicks on an item from the master.
Get the binding context from the selected item by getBindingContext(/*modelName*/).
Pass only key(s) to the navTo parameters (no need to pass the whole item context).
In the "Detail" view:
Attach a handler to the patternMatched event of the navigated route in the Detail controller's onInit.
In the handler, create the corresponding key, by which the target entry can be uniquely identified, by accessing the event parameter arguments in which the passed key(s) are stored. In case of OData, use the API createKey.
With the created key, call bindObject with the path to the unique entry in order to propagate its context to the detail view.
The relative binding paths in the detail view can be then resolved every time when the detail page is viewed. As a bonus, this also enables deep link navigation or bookmark capability.
You can also set local json model to store your data, and use it in the corresponding views. But be sure to initialize or clear it in the right time.