oData binding for Master and Detail table - sapui5

I have to bind data for master and detail table coming from oData service in UI5 application.
This is my odata URI:
http://********************/sap/opu/odata/sap/ServiceName/Logheaderset?$filter=Object eq 'zwwxx6' and (Aldate ge (datetime'2015-07-14T00:00:00') and Aldate le (datetime'2015-07-23T23:59:59'))&$expand=NavMess
My requirement is I want to show child Entityset in detail table and parent Entityset in master table.
I am able to show parent Entityset in master table its working fine but dont know how to show child Entityset in detail table.
This is my code for detail table:
oTable1.setModel(oModel1);
oFilter1 = new sap.ui.model.Filter("Object", sap.ui.model.FilterOperator.EQ, "zwwxx6");
oTable1.bindItems("/Logheaderset/Messagelinesset", oTemplate1,null,[oFilter1]);
var oPage2 = new sap.m.Page({
title : "Table Test Page",
enableScrolling : true,
content : [ oTable1 ]
});
rPannel1.addContent(oTable2);

It looks like you are trying to implement an application that looks like our default case: https://openui5.hana.ondemand.com/#docs/guide/5ca68e6e62e6464a8103297fbc19cd9c.html
The idea is to start navigation when clicking on a master-entry. In the routeMatched handler of the default page you set the binding of the detail page according to the arguments you gave to the navigation.
Essentially you use the ID of your entity as a navigation argument and then you can do something like oDetailView.bindElement("/SomeEntitySet(" + sEntityId + ")") in the handler.

Related

How to filter with NSPredicate by an attribute of an Element I pass from the parent view?

Please help me with the following question - how do I filter a fetchRequest by an attribute of an Element I pass from the parent view?
import SwiftUI
struct aListDetailView: View {
#State var aParameter: String
#FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "elementIdentificationAttribute == %#", aParameter)) var contracts: FetchedResults<Contract>
var body: some View {
List {...}
...}
I get "Cannot use instance member 'aContract' within property initializer; property initializers run before 'self' is available".
I understand why, but how do I bypass this?
My example is a simplified version.
In truth, my query is this: I have a list of Contract elements, and each contract has a list of invoices issued in regards to that specific contract. Each contract has a unique serial code I give it, as an attribute.
My #FetchRequest for a list of all Contract elements is in Content View. I loop through them to create a list. Each row of the list is a NavigationLink to a new view, and I pass the specific contract element to the new view.
In the new view, I need to fetch a list of all the invoices specific to that contract.
However, when I add a new invoice to create a new row in the invoice sub-view list, core data does not show the new row, for a new invoice. The new row appears only when I return to the Content View and back to the invoice view, because my #FetchRequest for Contracts is in Content View.
I thought I could bypass this issue through a #FetchRequest for a list of contracts that have an attribute of my choosing (say, an attribute contractSerialNumber) equal to the attribute instance of the contract I pass to this view.
Something like:
#FetchRequest that filters all contracts in storage by the specific, unique attribute of my choosing of the contract I passed from the Parent View. Places them in a list.
ForEach contract that has that particular attribute (which will be a list of one contract) {
ForEach contract.invoiceArray { invoice in
Text(contract.invoice.invoiceIdentificationAttribute
} }
Thank you!

UI5 - how to dynamically bind data to a Select in Table, depending on another combobox?

I have a classic situation - a table with two comboboxes (or, to be exact, sap.m.Select controls) and after select in the first one, I would like to have the values in the second one updated. This is my model, basically, the first combobox should contain the list of available states and once some is selected, the second sap.m.Select control should be populated by relevant cities
{
countries: [
{ country: 'France', cities: ['Paris', 'Marseille', 'Lyon']},
{ country: 'Germany', cities: ['Berlin', 'Bonn']}
]
}
The problem is that I dont know how to do it. I am able to get the id of the updated row using something like this.
onCountryChange: function (oEvent) {
const selectedItem = oEvent.getParameter("selectedItem");
var path = selectedItem.getBindingContext().getPath();
bindComboBox(path); // this should rebind the data, but not written yet
}
I know now I should rebind the data in the correct combobox, however, I don't know how to affect only that single combobox on the correct row and how to update it. Could someone advise me how to do it? The whole table is defined in the .xml view, can I do it also with a formatter or inline expression or is this scenario too difficult for that?
Thank you
You can use the bindAggregation method (from the ManagedObject) class to rebind the combo boxes' items.
onCountryChange: function (oEvent) {
const selectedItem = oEvent.getParameter("selectedItem");
var path = selectedItem.getBindingContext().getPath();
this.byId("combo2").bindAggregation("items", {
path: path + "/cities",
template: new sap.ui.core.Item({
key: "{}",
text: "{}"
})
});
}
Note: Replacing "combo2" with the id of your 2nd combo box/select control.
Edit: To get the correct combo box (assuming you have multiple created on a table, use the ID of the first combo box (oEvent.getSource().getId()) to generate the ID of the 2nd combo box. Without knowing more of the structure of the table (and how it's created) I can't offer more.

OData binding with $expand

We have to bind a OData url to UI5's ODataModel
https://sapes1.sapdevcenter.com/sap/opu/odata/sap/ZCD204_EPM_DEMO_SRV/BusinessPartners('0100000000')/SalesOrders/?$expand=SalesOrderItems
We are able to bind root level items that belong to each SalesOrder. However, we are running into problem in case of binding data from SalesOrderItems which are a child to SalesOrder.
We are not able to bind SalesOrderItems' fields to any of our objects. We tried using {SalesOrderItems/results/QuantityUnit}, {SalesOrderItems/QuantityUnit} without much luck.
Can you please suggest any alternatives?
There is 1..m cardinality between SalesOrder and SalesOrderItem
// model of oData
var model = sap.ui.model.odata.ODataModel("proxy/https/sapes1.sapdevcenter.com/sap/opu/odata/sap/ZCD204_EPM_DEMO_SRV/",true,'username','password');
//app is defined in index.html here we are setting model to the app.
App.setModel(model);
// create a table
var pastOrder_S3= new sap.m.Table("PastOrder_S3",{
inset:true,
//visibleRowCount: 2,
firstVisibleRow: 2,
fixedColumnCount: 2,
columns:[
new sap.m.Column({
header:new sap.m.Label("item").setText("Items"),
hAlign:"Left",
width:"20px",
demandPopin:true,
popinDisplay:"Block",
minScreenWidth: sap.m.ScreenSize.Medium
}),
new sap.m.Column({
header:new sap.m.Label("orderdetail").setText("OrderDetails"),
hAlign:"Left",
width:"200px",
demandPopin:true,
popinDisplay:"Block",
minScreenWidth: sap.m.ScreenSize.Medium
})
});
//create a template to bind into the table using model.
var oTemplate_S3= new sap.m.ColumnListItem({
type: sap.m.ListType.Active,
cells: [
new sap.m.Text({
text:"{ProductName} \n {ProductID}"
}),
new sap.m.Text({
text:"OrderId: {SalesOrderID} \n {DeliveryDate} \n {TotalSum}{Currency}"
})
]
});
// bind into the table.
`pastOrder_S3.bindAggregation("items","BusinessPartners('BusinessPartnerId')/SalesOrders/?$expand=SalesOrderItems",oTemplate_S3);`
Here we have a child property named 'SalesOrderItems' we need to read the properties inside the salesorderitems.
When binding the aggregation to an OData collection, the expand parameter (and any other parameters for that matter) has to be given separately, not part of the collection URI.
In an nutshell, you will have to do the following:
oTable.bindAggregation("items", {
path: "/BusinessPartners('BusinessPartnerId')/SalesOrders",
template: oTemplate,
parameters: {
expand: "SalesOrderItems"
}
});
Check out the bindAggregation and the ODataListBinding constructor documentation for more details.
Another problem that you will face is that you have this 1:n cardinality between the SaleOrder and the SaleOrderItems. This means that you will only be able to use this navigation property to bind an aggregation and not directly a property (so you need to have something like a table inside another table).
You will have to clearly define what should be displayed from the SaleOrderItems to be able to 'flatten' it (e.g. do an aggregation, or pick the first one, etc). This will most likely imply that changes must be made in the OData service or you will have to use either a custom control in UI5 or a JSONModel instead of / together with the OData one.

link multiple models on same row of sap.m.table

This may be a basic question, but it's my first, so please be kind :-).
I have a sap.m.table with two models, one model with transaction data (trxModel) and another model that is used to display a sap.m.select list (reasonCodeModel). The table model is set to trxModel.
The selected value key from the dropdown needs to update a value (ReasonCodeID) in the trxModel when a value from the reason code list is selected.
I can retrieve the selected key in the change event as so
var selKey = evt.getParameter("selectedItem").getKey();
Is there a simple way to find the trxModel relevant model path from the table row Select list value I've just modified? Or is it possible to bind the ReasonCodeID from the trxModel to the ReasonCodeID field in the reasonCodeModel?
Just an extra piece of info, The current row is selected and is accessible
var selItem = dtlTable.getSelectedItem();
2nd question and I guess could be kind of related, is there a way of getting the table model path based on the selected item (highlighted row) of the table? And vice a versa?
More details on Select & Table binding.
var tabTemplate = new sap.m.ColumnListItem(
{
::
new sap.m.Select(
"idReasonCodeSelect",
{
enabled : false,
change : function(evt) {
oS4View.getController().changeReasonCodeSel(evt);
}
}
),
Bind the resource code Select to the Table
// bind the reason codes to the reason code model
sap.ui.getCore().byId("idReasonCodeSelect").setModel(
oReasonCodeModel);
sap.ui.getCore().byId("idReasonCodeSelect").bindAggregation("items", "/results",
new sap.ui.core.Item({
key : "{ReasCodeID}",
text : "{ReasCodeDesc}"
}));
Per Qualiture comment, how do I bind the Select key to the table model ReasonCodeID value?
I found an approach to tackle the first part of my question above
From the change function on the Select, I can find the path of the table model using the following.
var path = evt.getSource().getParent().getBindingContext().sPath;
2nd Update:
On the selectionChange event on the table, there's a couple of options to find the associated model path or model content.
// find the model path
oModelPath = selItem.getBindingContext().getPath();
// model values
oItem = oEvent.getParameter("listItem").getBindingContext().getObject();
So my only remaining issue, While I loop through the table model results (trxModel) and I want the Select List (using setSelectedKey) to reflect the ReasonCodeID value in the trxModel.

Two model to a controller in sapui5

I have json model called contact this is how it looks:
{firstName:"",lastName:"",country:""}
I have an another model called country which contains the list of countries. I want this list of countries in a dropdown. While selecting a country from the dropdown, country field in contact model should get updated. How can i achieve that?
The simple steps to get this done are:
1. Set the Contact Model where ever you want such as View or Core.
2. Set the Country Model to DropDown list.
3. On selection of the DropDown list, on Change function you set Country field by the value you select in Dropdown list.
4. This will update the country value in the Contact Model.
you can use named data model and multimodel support
In your controller js,
this.getView().setModel(oCountryModel,"Country");
this.getView().setModel(ocontactModel ,"Contact");
If you want set Contact model with your default value, add logic in your init function:
init:function() {
//initialize contact model with default country
var oFilter = new sap.ui.model.Filter("country",
sap.ui.model.FilterOperator.EQ ,"America");
//suppose you have a Contact table called ContactList
var oTable =this.getView().byId("ContactList");
oTable.getBinding("items").filter([oFilter]);
}
In your view xml, use named model data binding, for example:
<Text text="{Contact>/firstName}"/>
Then you are using two models in your controller and view xml. Regarding dropdown selection change triggers an update for your contact, you need to attach event listener to the dropdown, and update the Contact data model based on the selected country. See the following code:
handleSelectionChange:function(oEvent) {
//get the new selected country
var country = oEvent.getParameters().newValue;
var oFilter = new sap.ui.model.Filter("country",
sap.ui.model.FilterOperator.EQ ,country);
//suppose you have a Contact table called ContactList
var oTable =this.getView().byId("ContactList");
oTable.getBinding("items").filter([oFilter]);
}