SAPUI5 Table cell valuestate disappear - sapui5

I am adding controls to SAPUI5 table column items via the controller using factory function so that I can apply cross-field validation along with standard control validations. Now the validation is working fine it shows the error message with the red coloured border, but when I move to next control, validation state of control disappear. I noticed that it is happening due to some internal functionality of SAPUI5 where it re-render the table body element of the table in the HTML dom explorer which also get rid of the error classes applied to control. It occurs for the first time, but when I try to change the value again of the same controller with an invalid data, it displays the error and keeps the value state with the red border.
my table XML view
<Table id="todosTable" growing="true" items="{
path: 'TripService>/todos', factory: '.populateItems'
}">
<columns>
<Column id="id">
<Text text="Id"/>
</Column>
<Column id="title">
<Text text="Title"/>
</Column>
<Column id="url">
<Text text="Url"/>
</Column>
<Column id="thumbnailUrl">
<Text text="Thumbnail Url"/>
</Column>
</columns>
</Table>
My Controller code to apply the columns item
function populateItems(sId: any, oContext: any) {
const idInput = new Input({
value: "{TripService>id}",
id: `id_${sId}`,
liveChange: onIdChange.bind(this)
});
const titleInput = new Input({
value: "{TripService>title}",
id: `title_${sId}`,
liveChange: onTitleChange.bind(this)
});
const urlInput = new Input({
value: "{TripService>url}"
});
const tumbnailInput = new Input({
value: "{TripService>thumbnailUrl}"
});
var row = new ColumnListItem(sId, {
cells: [idInput, titleInput, urlInput, tumbnailInput]
});
return row;
}
function onIdChange(oEvent: any) {
oEvent.oSource.setValueState(sap.ui.core.ValueState.Error);
}
function onTitleChange(oEvent: any) {
oEvent.oSource.setValueState(sap.ui.core.ValueState.Error);
}
Some images with valid error state and then with buggy error state
As you can see in above two images the error is gone in second image though I expect it to be there.

Related

Conditional formatting in table line

Hello dear colleagues,
I want to bold the total line and set highlight to red.
I am using the event modelContextChange:
<ColumnListItem vAlign="Middle" modelContextChange="onModelContextChange">
<cells>
<!--<ObjectIdentifier title="{Name}" text="{year}"/>
<Text text="{month}"/>-->
<Text text="{salesOrganization}" modelContextChange="onTextContextChange" />
<Text text="{product}" />
The event code:
onModelContextChange: function (oEvent) {
debugger
var oListItems = oEvent.getSource();
var oObject = oListItems.getBindingContext().getObject();
if (oObject.salesOrganization === "Total") {
// debugger
oListItems.setHighlight("Error");
// oText.addStyleClass("boldText");
} else {
oListItems.setHighlight("Information");
}
},
The issue with this approach is that when the context does not change the Highlight does not work properly. I´ve tried using custom formatted, but I could not make it work. Also, I could not find a way to read the row value to do the same logic above.
Would you give me any hint on how achieve it?
Thanks a lot
Pietro
View:
<Table mode="SingleSelectMaster" select="selectItem">
Controller:
selectItem: function(oEvent){
const oSelectedItem = oEvent.getSource().getSelectedItem();
oEvent.getSource().getItems().forEach(oItem => oItem.removeStyleClass("boldText")); //Remove bold class from all items
oSelectedItem.addStyleClass("boldText"); //Add class to selected item
},
CSS:
.boldText>td>span{
font-weight: bold !important;
}

ColumnListItem - Event Handler "press" NOT Triggered

I am using ColumnListItem to display a list of Sales Orders in Overview.view.xml. When the user clicks on an item of the list (of Sales Orders), the App should navigate to the Detail.view.xml.
I have defined the onPress event handler in Overview.Controller.js. But the App did not execute the function (I put an alert() there and it was not triggered). Why the onPress() is not triggered? How do I debug?
<Table items="{myOdata>/SalesOrderSet}">
<ColumnListItem type="Navigation" detailPress=".onPress">
<!-- ... -->
</ColumnListItem>
<columns>
<!-- ... -->
</columns>
</Table>
onPress: function (oEvent) {
//This code was generated by the layout editor.
alert("In");
var loOverview = "Data from Overview";
var oItem = oEvent.getSource();
var loRouter = sap.ui.core.UIComponent.getRouterFor(this);
loRouter.navTo("Detail", {
value: oItem.getBindingContext("oModel").getPath().substr(1)
});
},
The press function is not working as you have not written the correct handler for it. As per your code, the handler is written for detailPress. Just a typo, change the handler to press and it should just work.
Current:
<ColumnListItem type="Navigation" detailPress=".onPress">
Change required:
<ColumnListItem type="Navigation" press=".onPress">
the property that you should bind on the Table control is itemPress and your ColumnListItem need to have the type equals to Navigation
Can you check those?

ComboBox in UI5 does not display ValueState

ComboBox is not showing state like Error, Warning with highlight around the borders. But it does change the state. For example, if it is error state, and if I try to enter new value in combobox, it will show that "invalid Entry" tip near the box. But the box borders are never highlighted in red. Below is the code:
XML.view
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:l="sap.ui.layout">
<Dialog id......>
<ComboBox id="combo1" change="cChanged" items="{path: '/results'}">
<items>
<core:Item key="{ID}" text="{Name}"/>
</items>
</ComboBox>
</Dialog>
Controller.js
cChanged: function(oEvent) {
var newval = oEvent.getParameter("newValue");
var key = oEvent.getSource().getSelectedItem();
if (newval !== "" && key === null) {
sap.ui.getCore().byId("combo1").setValueState("Error");
oEvent.getSource().setValue("");
sap.m.MessageToast.show("Please select from existing IDs")
flag = false;
} else {
oEvent.getSource().setValueState('None');
}
You can also access combo1 control instance by using oEvent.getSource() event OR use byId() from the sap.ui.core.Fragment class and not sap.ui.getCore().byId()
Also, if you are writing a logic only to validate if what the user input in the combobox is a valid item, consider replacing your ComboBox by the sap.m.Select control.
Both ComboBox and Select has same look and feel, but Select does not allow a manual input. It can also have an empty option if you use the property forceSelection

Get path from property in m.table

I'm looking for a convenient method to get the path from a table cell.
Background: It is required to implement a search field allowing to filter on all columns of responsive table. Here, the path is needed as parameter for the filter object.
XML Code
<Table items="{path: 'modelName>pathPart1/pathPart2'}">
<headerToolbar>
<Toolbar>
<Title text="titleText"/>
<SearchField search="searchInTable"/>
</Toolbar>
</headerToolbar>
<columns>
<Column>
<Text text="column1"/>
</Column>
<Column>
<Text text="column2"/>
</Column>
</columns>
<ColumnListItem>
<Text text="{modelName>cellName1}"/>
<Text text="{modelName>cellName2}"/>
</ColumnListItem>
</Table>
Controller Logic
searchInTable: function(event) {
var table = event.getSource().getParent().getParent();
var query = event.getParameters("query");
table.getBinding("items").filter(this.getFilters(table, query));
},
getFilters: function(table, query) {
var aFilters = [];
var items = table.getItems();
// Loop through items aggregation and populate filter object
jQuery.each(items, function(i, oItem) {
// Get path from cells (e.g. cellName1)
var sPath = oItem.mAggregations.cells[i].mBindingInfos.text.binding.sPath;
var sOperator = FilterOperator.EQ;
var sValue1 = query;
var oFilter = new Filter(sPath, sOperator, sValue1);
aFilters.push(oFilter);
});
return aFilters;
},
Can we replace this part by a more convenient and robust method?
var sPath = oItem.mAggregations.cells[i].mBindingInfos.text.binding.sPath;
As you notice, I'm trying to receive the sPath going through the whole object. However, its not working in all cases as the structure of the object may change. I bet there is an better approach available. However, I struggling a bit here.
Any ideas?
Edit: I do like to get the path pointing to the text property in the table. In this samplle it would be: cellName2
I'm on the phone right now, so I can't test it, but it is something like this
oItem.getCells()[i].getBindingContext().getPath()
getCells() comes from the ColumnListItem API if I am not wrong.
The other two from the ODataListBinding API or something like that...
If you dive a bit in the API you will find it
EDIT: I think you should provide the model name when getting the context. But I don't remember well...
oItem.getCells()[i].getBindingContext("modelName").getPath()
Try both, with and without it...
EDIT2: Here you have the snippet http://jsbin.com/votaxiyedi/edit?html,output
And this what you need:
oItem.getBindingContext("odata").getPath() + "/" + oItem.getCells()[0].getBinding("text").getPath();

How to pass control reference to formatter in XMLView?

In SAPUI5's JSView, it is quite easy to pass the current control reference to a formatter function:
oTable.bindItems("/rows", new sap.m.ColumnListItem({
cells : [ new sap.m.Text().bindProperty("text", {
parts: [
{ path: "someInteger" }
],
formatter: function(iValue) {
var idText = this.getId(); //this references the current control
return iValue;
}
})]
}));
(The 'easy' part of course is because this is referenced in the control's inner formatter function)
However, with XMLViews I haven't managed yet to get a reference to the current control in the formatter function:
<Table items="{/rows}">
<columns>
<Column>
<Text text="Some Integer" />
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{ path : 'someInteger', formatter : '.formatCell' }" />
</cells>
</ColumnListItem>
</items>
</Table>
And the formatter:
formatCell : function (sValue) {
var a = this; //this references the controller
return sValue;
}
Anyone knows how to make this work in XMLViews?
Define your formatter functions in a separate file. Then this will be the Control whose property is being formatted.
my/own/Formatter.js:
sap.ui.define(function () {
"use strict";
return {
formatCell: function (iValue) {
var idText = this.getId(); //this references the current control
return iValue;
}
};
});
View:
<Table items="{/rows}">
<columns>
<Column>
<Text text="Some Integer" />
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{ path : 'someInteger', formatter : 'my.own.Formatter.formatCell' }" />
</cells>
</ColumnListItem>
</items>
</Table>
The answer of #codeworrior in this issue make it more clear:
A name that starts with a dot (e.g. ".foo") is searched for in the controller of the view and the execution context will be enforced to being the controller.
All other names are resolved starting from the window object and they get the control/element as context which holds the binding.
Just complement #hirse's answer, and for those who get formatter function xxx not found error:
both .formatter.myformatter and mynamespace.Formatter.myformatter is working.
The logic of parse formatter is in sap.ui.base.BindingParser.resolveRef(oBindingInfo,'formatter')
BindingParser seems different in sapUI5(1.54) and openUI5. I'll take sapUI5 version as an example.
If formatter name starts with a dot ('.'), eg. .formatter.myformatter, lookup will start with the given context(the Controller of the view), otherwise("mynamespace.Formatter.myformatter") it will start with the global context (window).
and jQuery.sap.getObject("formatter.myformatter", oContext) or jQuery.sap.getObject("mynamespace.Formatter.myformatter", window) is called.
so If you get formatter function xxx not found! error. set a break point in jQuery.sap.getObject, and check if there is "myformatter" in oContext or window object.
And I found that there is no mynamespace.Formatter.myformatter in my window object. so I change my formatter from
sap.ui.define([], function() {
return {
myformatter: function () {}
}
})
To
sap.ui.define([], function() {
var Formatter = {
myformatter: function () {}
}
return Formatter
}, /* bExport */ true)
And it's working.
Formatter must be defined with variable. Formatter reference must be included in controller. Formatter must be referenced with absolut path.
Formatter.js
sap.ui.define([], function () {
var Formatter = {
myFormatter: function () {
return "";
}
}
return Formatter }, /* bExport */ true)
View.controller.js
sap.ui.define([
...
"com/my/company/utils/Formatter"], function (..., Formatter) {
"use strict";
return Controller.extend("com.my.company.View", {
View.view.xml
<GenericTag status="{path: 'MyStatus', formatter: 'com.my.company.utils.Formatter.myFormatter'}/>