How to iterate a nested model and generate Elements based on that - sapui5

As the title states, I do have a Model, with Choices property which is nested.
let modelExample = {
0: {
"Description": "Example 0",
"Choices": [ "0", "1", "2" ]
},
1: {
"Description": "Example 1",
"Choices": [ "0", "1", "2" ]
},
2: {
"Description": "Example 2",
"Choices": [ "0", "1", "2" ]
}
};
It should construct a Table with Description as a Label and Choices as Buttons, something like:
Table:
<Table items="{modelExample>/}">
<columns>
<Column>
<Text text="Description" />
</Column>
<Column>
<Text text="Choices" />
</Column>
</columns>
<ColumnListItem>
<Label text="{modelExample>Description}" />
<SegmentedButton selectedButton="none">
<items>
<SegmentedButtonItem text="{modelExample>Choices/0}" />
<SegmentedButtonItem text="{modelExample>Choices/1}" />
<SegmentedButtonItem text="{modelExample>Choices/2}" />
</items>
</SegmentedButton>
</ColumnListItem>
</Table>
Issue: As it stands right now, it does work in this way. The problem is that; if you were to extend Choices to 20+ entries, it would also require to add 20+ SegmentedButtonItem, and that is something I try to avoid.
Question: How to generically generate Buttons based on a nested Choices-Property / How to also iterate through nested Choices?

Set your SegementedButton item path to {modelExample>Choices}. The path of your text property in SegmentedButtonItem is modelExample> because there's no key/value in your array.
<SegmentedButton items="{
path: 'modelExample>Choices',
templateShareable: false
}">
<items>
<SegmentedButtonItem text="{modelExample>}" />
</items>
</SegmentedButton>
In case of an array like [{0:"0"},{1:"1"},{2:"2"}] (Your example in your comment isn't valid) you should prepare you're array to receive a static property.
const aArray = [{0:"0"},{1:"1"},{2:"2"}];
const aPreparedArray = aArray.map(oObject =>{ //Loop through array
for(let key in oObject){ //Loop through object
return {propertyName: oObject[key]}; /return object with a static property
}
});
modelExample.setProperty("/", aPreparedArray); //Set array to model
Then you could use:
<SegmentedButton items="{
path: 'modelExample>Choices',
templateShareable: false
}">
<items>
<SegmentedButtonItem text="{modelExample>propertyName}" />
</items>
</SegmentedButton>

Related

Table grouping resets selected values when adding new item

Here we have a very basic table with an Add button and grouping activated. When I select values for the existing items and then press the button to add a new row, my selected values are getting reset, but without grouping it works fine. Am I doing something wrong or is this a bug?
sap.ui.getCore().attachInit(() => sap.ui.require([
"sap/ui/core/mvc/XMLView",
"sap/ui/model/json/JSONModel" // sample model. Can be also an ODataModel.
], async (XMLView, JSONModel) => {
"use strict";
const control = await XMLView.create({
definition: `<mvc:View xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
<Table items="{
path: '/tableItems',
sorter: { path : 'group', group: true }
}">
<headerToolbar>
<OverflowToolbar>
<Title text="Test Table" />
<ToolbarSpacer/>
<Button id="addButton" text="Add" type="Emphasized" />
</OverflowToolbar>
</headerToolbar>
<columns>
<Column width="20rem">
<Label text="col0"/>
</Column>
<Column width="20rem">
<Label text="col1"/>
</Column>
</columns>
<ColumnListItem vAlign="Middle">
<Text text="{text}" />
<Select forceSelection="false" width="100%" xmlns:core="sap.ui.core">
<core:Item text="test1" key="test1" />
<core:Item text="test2" key="test2" />
<core:Item text="test3" key="test3" />
<core:Item text="test4" key="test4" />
</Select>
</ColumnListItem>
</Table>
</mvc:View>`,
afterInit: function() {
this.byId("addButton").attachPress(onPressAdd, this);
function onPressAdd() {
const oModel = this.getModel();
const aItems = oModel.getProperty("/tableItems");
const newItems = aItems.concat({
group: "3",
text: "3-1",
key: "3-1",
});
oModel.setProperty("/tableItems", newItems);
}
},
models: new JSONModel({
number: 0,
tableItems: [
{
group: "1",
text: "1-1",
key: "1-1",
},
{
group: "1",
text: "1-2",
key: "1-2",
},
{
group: "2",
text: "2-1",
key: "2-1",
},
{
group: "2",
text: "2-2",
key: "2-2",
},
],
}),
});
control.placeAt("content");
}));
<script id="sap-ui-bootstrap"
src="https://openui5.hana.ondemand.com/1.82.2/resources/sap-ui-core.js"
data-sap-ui-libs="sap.ui.core,sap.m"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-compatversion="edge"
data-sap-ui-async="true"
data-sap-ui-excludejquerycompat="true"
data-sap-ui-xx-waitfortheme="init"
></script>
<body id="content" class="sapUiBody sapUiSizeCompact"></body>
Set the growing="true" to the sap.m.Table.
This enables GrowingEnablement internally which prevents rerendering the entire ListBase (Table) when the target gets invalidated. Only the necessary item will be then appended to the table.
Generally, in order to optimize the rendering behavior, it's always a good practice to..:
Enable the growing property if the table / list is editable, shrinkable, or expandable.
Add key: <propertyName with unique values> to the ListBinding info object to benefit from the Extended Change Detection if the model is a client-side model such as JSONModel. With an ODataModel, the key is automatically added by the framework.
<Table xmlns="sap.m"
growing="true"
items="{
path: 'myModel>/myCollection',
key: <propertyName>, (if 'myModel' is not an ODataModel)
sorter: ...
}"
>

How to Remove Row from Table

This question is a follow up to this: Button to add new row in SAPUI5 table
In my new scenario, I have added a "remove" button in the first column of the table. Again, the JSON file looks like this:
{
"Invoices": [
{
"ProductName": "Pineapple",
"Quantity": 21,
"ExtendedPrice": 87.2000,
"ShipperName": "Fun Inc.",
"ShippedDate": "2015-04-01T00:00:00",
"Status": "A"
},
...
]
}
My view now looks like this:
<mvc:View controllerName="stepbystep.demo.wt.controller.App" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:core="sap.ui.core"
xmlns:html="http://www.w3.org/1999/xhtml" displayBlock="true">
<Table id="ins" items="{ path : 'invoice>/Invoices', sorter : { path : 'ProductName' } }">
<headerToolbar>
<Toolbar>
<Button icon="sap-icon://add" text="Row" press="addRow"/>
<Button icon="sap-icon://display" text="Row" press="fetchRecords"/>
</Toolbar>
</headerToolbar>
<columns>
<Column width="50px"/>
<Column hAlign="Right" minScreenWidth="Small" demandPopin="true" width="4em">
<Text text="{i18n>columnQuantity}"/>
</Column>
<Column>
<Text text="{i18n>columnName}"/>
</Column>
<Column minScreenWidth="Small" demandPopin="true">
<Text text="{i18n>columnStatus}"/>
</Column>
<Column minScreenWidth="Tablet" demandPopin="false">
<Text text="{i18n>columnSupplier}"/>
</Column>
<Column hAlign="Right">
<Text text="{i18n>columnPrice}"/>
</Column>
</columns>
<items>
<ColumnListItem type="Navigation" press="onPress">
<cells>
<Button icon="sap-icon://delete" press="deleteRow" type="Reject"/>
<ObjectNumber number="{invoice>Quantity}" emphasized="false"/>
<ObjectIdentifier title="{invoice>ProductName}"/>
<Text text="{ path: 'invoice>Status', formatter: '.formatter.statusText' }"/>
<Text text="{invoice>ShipperName}"/>
<ObjectNumber
number="{ parts: [{path: 'invoice>ExtendedPrice'}, {path: 'view>/currency'}], type: 'sap.ui.model.type.Currency', formatOptions: { showMeasure: false } }"
unit="{view>/currency}" state="{= ${invoice>ExtendedPrice} > 50 ? 'Error' : 'Success' }"/>
</cells>
</ColumnListItem>
</items>
</Table>
And I have a function for adding a row (this one works fine):
addRow: function() {
this.getView().getModel("invoice").create("/Invoices", {
ProductName: "",
Quantity: "",
ShippedDate: "",
Status: ""
});
}
And I am trying to build one for deleting. This is what I have so far:
deleteRow: function(oArg) {
// var array = oArg.oSource.sId.split("-");
// var index = array[array.length - 1];
var path = oArg.oSource.oPropagatedProperties.oBindingContexts.invoice.sPath;
delete this.getView().getModel("invoice").oData[path.substr(1)];
this.getView().getModel("invoice").refresh(true);
}
But the row gets emptied and then returns again (like getting fetched from the mock server again). I am trying to completely remove the row (not just its contents), and the data to be removed.
I have seen plenty of examples online, but none cover my use case.
If you already use create for create new row then I think the best is to be consistent and use remove in order to remove it.
so in your case I think your code should look something like this:
this.getView().getModel("invoice").remove(path);
This line will do both:
Execute DELETE request to delete the object on the server (if you are using oDataModel)
Will delete the row from the table and refresh it because the table is bounded to this model
If you can please always use binding in your code. Using binding is much more efficient and easy to maintain because you don't need to deal with any DOM objects. The only thing that you need to do in code is creating/deleting/updating your model objects and UI5 runtime will do the rest for you.

bind entity to get single record [duplicate]

This question already has answers here:
bindProperty to Single OData Entity
(2 answers)
Closed 18 days ago.
How to fetch single entity record data please find the code below
binding in xml table.am doing like below
table in xml view
<Table items="{/dum}" id="table" width="auto">
When you can bind the url directly to the table:
If I give like below am getting all records.
var url = "/myentity";
var table = oView.byId("table");
table.bindItems({
path: url,
template: table.getBindingInfo("items").template
});
If I give var url = "/myentity('srujan')";
am not able to get particular user data
HTTP request failed400,Bad Request,{"error":{"code":"005056A509B11EE1B9A8FEC11C23378E","message":{"lang":"en","value":"System query options '$orderby,$skip,$top,$skiptoken,$inlinecount' are not allowed in the requested URI"
Here's the snippet that shows how bindElement ("binding" in the XML) it works for your case:
var oModel = new sap.ui.model.json.JSONModel({
"Product(1)": {
Price: 100,
Name: "Product # 1"
},
Products: [{
Price: 200,
Name: "Product # 1"
}, {
Price: 500,
Name: "Product # 2"
}, {
Price: 542,
Name: "Product # 3"
}]
});
sap.ui.getCore().setModel(oModel);
sap.ui.xmlfragment({
fragmentContent: document.getElementById("table").textContent
}).placeAt("content");
<!DOCTYPE html>
<html>
<body>
<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"></script>
<script id="table" type="sapui5/xml">
<Table xmlns="sap.m" binding="{/Product(1)}">
<columns>
<Column>
<Label text="Binding" />
</Column>
<Column>
<Label text="Column 2" />
</Column>
<Column>
<Label text="Column 3" />
</Column>
</columns>
<items>
<ColumnListItem>
<Text text="/Products(1) on table" />
<Text text="{Name}" />
<Text text="{Price}" />
</ColumnListItem>
<ColumnListItem binding="{/Products/2}">
<Text text="/Products/2 on item" />
<Text text="{Name}" />
<Text text="{Price}" />
</ColumnListItem>
</items>
</Table>
</script>
<div id="content"></div>
</body>
</html>

SAPUI5: Refreshing table contents after oData.read

Im building an application with two textboxes, a button and a table. On pressing the button, an array of filters is composed from the contents of the textboxes and sent to my oData service in a read request. Right now, when one record is returned (as it shows in the console), the table will not be updated. What am I missing?
This is the table:
<Table id="PLTab" items="{/ORDSET}">
<headerToolbar>
<Toolbar>
<Title text="Planned Orders" level="H2" />
</Toolbar>
</headerToolbar>
<columns>
<Column>
<Text text="Product" />
</Column>
<Column>
<Text text="Planned Order" />
</Column>
<Column>
<Text text="Production Planner" />
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<ObjectIdentifier title="{Maktx}" text="{Matnr}" />
<Text text="{Ordno}" />
<Text text="{Name}" />
</cells>
</ColumnListItem>
</items>
</Table>
And this is the relevant part of the controller:
onInit: function() {
var oModel = this.getOwnerComponent().getModel("path");
oModel.refresh(true);
this.getView().setModel(oModel);
},
openOrders: function(oEvent) {
var PLFilters = [];
PLFilters.push(new sap.ui.model.Filter({
path: "Matnr",
operator: sap.ui.model.FilterOperator.EQ,
value1: this.getView().byId("Product").getValue()
}));
PLFilters.push(new sap.ui.model.Filter({
path: "Locno",
operator: sap.ui.model.FilterOperator.EQ,
value1: this.getView().byId("Location").getValue()
}));
var oModel = this.getOwnerComponent().getModel("path");
oModel.read("/ORDSET", {
filters: PLFilters,
success: function(oData, oResponse) {
console.log(oData);
}
});
}
Thanks & regards,
Max
var oModel = this.getOwnerComponent().getModel("path");
You use the model named "path" to read data, but in your XML View you use the default model (unnamed).
Try to change to
var oModel = this.getOwnerComponent().getModel();

How can I get row value for each sap.m.select element

<m:Table id="tableId"
inset="false"
mode="MultiSelect"
width = "100%"
fixedLayout="false"
border-collapse="collapse"
items="{
path: 'jsonViewModel>/results',
sorter: {
path: 'ProductId'
}
}">
<columns>
<Column
minScreenWidth="Desktop"
demandPopin="true">
<Text text="Product No" />
</Column>
<Column
minScreenWidth="Desktop"
demandPopin="true"
hAlign="Left">
<Text text="Model" />
</Column>...
</columns>
<items>
<ColumnListItem>
<cells>
<ObjectIdentifier
title="{jsonViewModel>ProductId}"/>
<Select id="selectId"
items="{
path: '/ModelList',
sorter: { path: 'Name' }
}">
<core:Item key="{modelId}" text="{Name}" />
</Select>...
</cells>
</ColumnListItem>
</items>
</Table>
First I have a jsonViewModel which is holding Products JSON array and also there is a ModelList service which gives me the list of models. So I should be able to fill some inputs(I didn't show other inputs cause I can retrive their values) and select model of products. But if I have 5 products I also have 5 select elements and I can't retrieve the select item for each rows(for each products). For example I can't retrieve the value with these codes in controller:
var oSelect = this.getView().byId("selectId");
var selectedItemObject = oSelect.getSelectedItem().getBindingContext().getObject();
var selectedModelName = selectedItemObject.Name;
Cause I have 5 select elements indeed and with these codes I can't retrieve every selected item object. Any help would be appreciated.
Cant we go through every row and then fetch the select control and then fetch the selectedItem? I mean,
var aItems = this.getView().byId("tableId").getItems();
for(var i =0;i<aItems.length;i++){
var aCells = aItems[i].getCells();
// I know select is at 0th cell, so I can use aCells[0].
var rowSelectedKey = aCells[0].getSelectedItem().getKey();
// once you have the selcetedKey, you can save them in a local array or // model and after the loop, you can work with them
}