How to Remove Row from Table - sapui5

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.

Related

How to implement multiple level merge in sap.m.Table?

DEMO
I want to implement multiple level merge in sap.m.Table. I tried to add mergeDuplicates="true" in the second column, but it look like this:
Pretty strange. I added data of the first column to the second column: <Text text="{name} {amount} "/>, the split looks like want I want, but how to hide {name} data?
Did some research in doc of sap.m.Column, find mergeFunctionName
You can pass one string parameter to given function after "#" sign. e.g. "data#myparameter":
Can I pass both {name} and {amount} to myparameter function?
In order to create a property where we can add a property to hold such primary key that can automatically be accessed by a single getter function, we extend the Text control as shown below.
sap.ui.define(
['sap/m/Text'],
function(Text) {
return Text.extend("namespace.controls.MergableText", {
metadata: {
properties: {
mergeDependent: {
type: "string",
defaultValue: ""
}
}
},
renderer: function(oRm, oControl) {
sap.m.TextRenderer.render(oRm, oControl); //use supercass renderer routine
}
});
}
);
The above is a file called MergableText.js which can be stored in the controls folder in the webapp. Now this can be used by defining in namespace:
<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:custom="namespace.controls"
controllerName="namespace.controller.PlannedOT">
<Table>
<columns>....</columns>
<items>
<ColumnListItem>
....
<custom:MergableText text="{>amount}" mergeDependent="{name}"> </custom:MergableText>
.....
<ColumnListItem>
</items>
</Table>
</mvc:View>
In this manner the amount can be merged only based on its dependency on the name.
the root cause is that you should define your sorter properly for your binding.
items="{
path: '/',
sorter: [{
path: 'name',
descending: false,
group:true
}, {
path: 'amount',
descending: false,
group:true
}]
}"
Using mergeNameFunction is the good approach :) You can use any binding internal method that will force the comparison.
E.g.
<Column mergeDuplicates="true" mergeFunctionName="getBindingContext">
<Text text="column2"/>
</Column>
will work with no other modifications
Late reply but hopefully still useful. I developed my own solution based on this.
Good news: What you want to achieve is possible without extending controls or grouping the table.
Add sap.ui.core library:
xmlns:core="sap.ui.core"
Add custom data to your cells like this:
<customData>
<core:CustomData key="mergeKey" value="{name}" />
</customData>
Use this custom data in your merge function like this:
mergeFunctionName="data#mergeKey"
The magic happens when you use the binding from column1 and column2 combined as a value for the custom data in column 2. That stops the whole column2 from becoming one cell and you get the desired divider between bus and truck.
Final solution:
<mvc:View
controllerName="demo.FirstPage"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
xmlns:semantic="sap.f.semantic"
xmlns:core="sap.ui.core">
<Table
id="table"
mode="MultiSelect"
width="auto"
items="{
path: '/'
}"
growing="true"
growingScrollToLoad="true">
<columns>
<Column
mergeDuplicates="true"
mergeFunctionName="data#mergeKey">
<Text text="column1"/>
</Column>
<Column
mergeDuplicates="true"
mergeFunctionName="data#mergeKey">
<Text text="column2"/>
</Column>
<Column>
<Text text="column3"/>
</Column>
</columns>
<items>
<ColumnListItem id="cli" type="Detail">
<cells>
<Text text="{name}">
<customData>
<core:CustomData key="mergeKey" value="{name}" />
</customData>
</Text>
<Text text="{amount}">
<customData>
<core:CustomData key="mergeKey" value="{name}{amount}" />
</customData>
</Text>
<Text text="{currency}, {size}"/>
</cells>
</ColumnListItem>
</items>
</Table>
</mvc:View>
Screenshot of final result

sap ui5 with json data binding

If I have json data like this
[{"processor":"Mr. XYZ","components":["asd","efg","ghi","fjk"]} ,
{"processor":"Mr. XYZ","components":["asd","efg","ghi","ghi"]} ,
{"processor":"Mr. XYZ","components":["asd","efg","lkl"]} ]
If I am binding this to a table:
<Table id="myt1" items="{path: '/'}">
<columns>
<Column>
<Label text="Processor"/>
</Column>
<Column>
<Label text="Components"/>
</Column>
</columns>
<items>
<ColumnListItem>
<Text text="{processor}"/>
<Text text="{components}"/>
</ColumnListItem>
</items>
</Table>
How do I bind the array of components in separate lines in a cell for a processor in that table ?
Please refer the image for the output I am looking for.
Thanks in advance !
You could use a text formatter to add a new line after each array element.
<ColumnListItem>
<Text text="{processor}"/>
<Text text="{
path: 'components',
formatter: '.formatter.formatText'
}"/>
</ColumnListItem>
Formatter:
sap.ui.define([], function () {
"use strict";
return {
formatText : function(s){
var sOut = "";
s.forEach(function(sTxt){
sOut += sTxt + "\n";
});
return sOut;
}
}
});

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
}

Getting the bound data object of pressed table row - getBindingContext() returns undefeind

In SAP UI5, I try to get the data object (in my controller) that is bound to a table row when the user presses it. My view is defined in XML, and my controller is in JS of course.
I have checked How to get content of a row in sap.m.table already but it doesn't work for me, or something is missing.
My view (the relevant part):
<Panel>
<Table id="lineItemList" items="{
path: 'statusJobs>/jobs',
sorter: {
path: 'start',
descending: true
}
}">
<headerToolbar>
<!-- ... -->
</headerToolbar>
<columns>
<Column hAlign="Left" vAlign="Middle">
<Label text="Job" />
</Column>
<Column hAlign="Center" vAlign="Middle">
<Label text="Start" />
</Column>
<Column hAlign="Center" vAlign="Middle">
<Label text="End" />
</Column>
<Column hAlign="Right" vAlign="Middle">
<Label text="Success" />
</Column>
</columns>
<ColumnListItem
type="Navigation"
press=".handleLineItemPress"
>
<Text text="{statusJobs>job}" />
<Text text="{
path: 'statusJobs>start',
formatter:'util.Formatter.Date'}"
/>
<Text text="{
path: 'statusJobs>end',
formatter: 'util.Formatter.Date'}"
/>
<Text text="{statusJobs>status}"/>
</ColumnListItem>
</Table>
The relevant part here is obviously:
<ColumnListItem
type="Navigation"
press=".handleLineItemPress"
>
And in my controller, I have this:
handleLineItemPress: function(evt) {
console.log('evt.getSource: ' + evt.getSource());
console.log('evt.getBindingContext: ' + evt.getSource().getBindingContext());
}
which logs as follows:
evt.getSource: Element sap.m.ColumnListItem#__item11-StatusJobs--lineItemList-0
evt.getBindingContext: undefined
evt.getSource returns the ColumnListItem, so of course from there, I could use the object hierarchy and fetch the text of a cell, like:
evt.getSource().getCells()[0].getText();
But this does not seem to be the right way and especially does not give the the entire object nor its unique ID, which I happen to not display in the table.
I am somehow missing the connection back to my data model, that I had bound earlier in the code, in the <Table> item, as follows:
items="{
path: 'statusJobs>/jobs',
sorter: {
path: 'start',
descending: true
}
}"
I hate to say it, but I have had the same issue, and it took me quite some time to find the cause...
It's all related to the use of named models, in your case statusJobs.
If you want to retrieve the binding context for items bound to a named attribute, for some reason (honestly, I can't think of any) you also have to specify the named model:
evt.getSource().getBindingContext("statusJobs");
will return the correct binding context.
Thus, to retrieve the actual object bound to the pressed line item, you could then use:
var obj = evt.getSource().getBindingContext("statusJobs").getObject();
Since a context can't be bound to more than one model (to my knowledge) I really don't understand why you need to specifically give the named model name as a parameter, but for now I guess we have to live with this behavior