How to pass control reference to formatter in XMLView? - sapui5

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'}/>

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;
}

Suggestion Items not appearing after Value State Change in sap.m Table input

I have a SuggestionItems on sap.m table input which is not appearing after value state change.
Please note that SAPUI5 Version used is 1.38.37
I have a sap.m table with columns with Suggestion List enabled. User has to select customer number from that available suggestion list. Also he can enter manually.
Now there are list of validations depending on user input.
XML.view
<items>
<ColumnListItem>
<cells>
<Input id="idCustNumber" type="Text" value="{path:'createJSON>CUSTNO'}" change="onCustValChange" liveChange="onLiveChangeCustNo"
startSuggestion="3" maxSuggestionWidth="400px" showSuggestion="true" suggestionItemSelected="OnSuggestionSelectedCustNumber"
suggest="handleSuggestCustNo" suggestionItems="{path:'ITEMVALJSON>/items', templateShareable:false}">
<suggestionItems>
<core:Item key="{ITEMVALJSON>CustNoAndName}" text="{ITEMVALJSON>CustNoAndName}"/>
</suggestionItems>
</Input>
Controller.js
handleSuggestCustNo: function (oEvent) {
var sTerm = oEvent.getParameter("suggestValue");
oEvent.getSource().setFilterFunction(function (sTerm, oItem) {
// A case-insensitive 'string contains' style filter
return oItem.getText().match(new RegExp(sTerm, "i"));
});
},
onCustValChange: function (oEvent) {
var oInput = oEvent.getSource();
this._validateCustValInput(oInput);
},
_validateCustValInput: function (oInput) {
var oBinding = oInput.getBinding("suggestionItems");
var value = oInput.getValue();
if (value !== "") {
var filters = [new Filter("CustNo", sap.ui.model.FilterOperator.EQ, value)];
var found = oBinding.filter(filters).getLength();
if (found === 0) {
oInput.setValueState(sap.ui.core.ValueState.Error);
this.sSelectedTableRow.ERRORFLAG = "X";
oInput.setValueStateText("Customer " + "'" + value + "' is invalid for GCC: " + "'" + this.Header.Cntry + "'");
} else {
oInput.setValueState(sap.ui.core.ValueState.None);
this.sSelectedTableRow.ERRORFLAG = "";
}
} else {
oInput.setValueState(sap.ui.core.ValueState.None);
this.sSelectedTableRow.ERRORFLAG = "";
}
},
Now issue is, if validation is failing, I am setting the value state to error. then after value change, suggestion list is not appearing.
Suggestion List Error
The problem is not the validation itself (or the SAPUI5 version) but in this line of code:
var found = oBinding.filter(filters).getLength();
Here you are filtering out your suggestionItems aggregation.... That's why they stop working: the ITEMVALJSON model is only filled with the chosen and validated value. Try to cancel a letter of the bound value in the input and you will see what I mean.
By the way I noticed two more things: you are filtering the wrong property (but maybe is only a matter of cut and paste) and your handleSuggestCustNo function is probably unnecessary. You can set the filter function (setFilterFunction) only once for your input in the initialization phase.
Hope this helps.
My (minimal) working code,I tried it with 1.38.xx
<Table id="table" items="{ path: '/items'}">
<columns>
<Column>
<Text text="ItemNo"/>
</Column>
<Column>
<Text text="Name"/>
</Column>
</columns>
<ColumnListItem>
<cells>
<Text text="{ItemNo}"/>
<Input id="idCustNumber" placeholder="Suggestion test 2" type="Text" _value="{path:'localModel>CUSTNO'}" change="onCustValChange"
maxSuggestionWidth="400px" showSuggestion="true" suggest="handleSuggestCustNo"
suggestionItems="{path:'localModel>/items', templateShareable:true}">
<suggestionItems>
<core:Item text="{localModel>CustNoAndName}"/>
</suggestionItems>
</Input>
</cells>
</ColumnListItem>
</Table>
Controller code:
onInit: function () {
var oLocalModel = new sap.ui.model.json.JSONModel({
items: [{
CustoNo: 1,
CustNoAndName: "Mickey Mouse"
}, {
CustoNo: 2,
CustNoAndName: "Donald Duck"
}]
});
this.getView().setModel(oLocalModel, "localModel");
var oData = {
items: [{
ItemNo: 1
}, {
ItemNo: 2
}, {
ItemNo: 3
}]
};
var oModel = new sap.ui.model.json.JSONModel(oData);
this.getView().setModel(oModel);
},
handleSuggestCustNo: function (oEvent) {
console.log("handleSuggestCustNo");
var sTerm = oEvent.getParameter("suggestValue");
console.log({
sTerm
});
},
onCustValChange: function (oEvent) {
console.log("onCustValChange");
var oInput = oEvent.getSource();
this._validateCustValInput(oInput);
},
_validateCustValInput: function (oInput) {
console.log("_validateCustValInput");
var oBinding = oInput.getBinding("suggestionItems");
var value = oInput.getValue();
if (value !== "") {
var filters = [new sap.ui.model.Filter("CustNoAndName", sap.ui.model.FilterOperator.EQ, value)];
var found = oBinding.filter(filters).getLength();
if (found === 0) {
oInput.setValueState(sap.ui.core.ValueState.Error);
//this.sSelectedTableRow.ERRORFLAG = "X";
oInput.setValueStateText("Customer " + "'" + value + "' is invalid");
} else {
oInput.setValueState(sap.ui.core.ValueState.None);
//this.sSelectedTableRow.ERRORFLAG = "";
}
} else {
oInput.setValueState(sap.ui.core.ValueState.None);
//this.sSelectedTableRow.ERRORFLAG = "";
}
oBinding.filter([]);
}
The "value" property of each input is commented. Can you try this code and tell me if it works for you? Maybe I got something wrong but my _validateCustValInput function is always triggered.

How to set the input of a combobox to read-only

In one of my UI5-Dialogs, I implemented a combobox which is invisible when the screen is initially loaded.
In the method onAfterRendering, I start with setting the input to read-only:
onAfterRendering: function(oEvent) {
var oShovel = this.getView("View0200").byId("comboShovel");
oShovel.$().find("input").attr("readonly", true);
this.setVisibleByListKey();
},
After this the method setVisibleByListKey is called, the property visibleShovel will be set to false.
setVisibleByListKey: function(oEvent) {
var oModel = this.getView("View0200").getModel("Data0200");
this.setVisibleByListKey1(oModel);
// ...
},
setVisibleByListKey1: function(oModel) {
oModel.setProperty("/visibleShovel", false);
},
The property is bound to the attribute visible on my combobox.
Because of this behavior, the method onAfterRendering will be called again, the attribute readonly is not available (because of invisibility).
<ComboBox id="comboShovel"
editable="true"
enabled="true"
visible="{Data0200>/visibleShovel}"
valueState="None"
change=".changeCombo">
<items>
<core:Item text="Ja" enabled="true" key="0" />
<core:Item text="Nein" enabled="true" key="1" />
<core:Item text="Nicht erforderlich" enabled="true" key="2" />
</items>
</ComboBox>
I tried to call the set method in onInit or onBeforeRendering but at this time the input attributes can not be changed (because of invisibility again).
So how can I set the input of the combobox to read-only when I set the named visible property?
Solution would be either to use sap.m.Select or to implement a "change" event handler for the sap.m.Combobox and use a coding similar to this sample:
handleChange: function(oEvent) {
var oValidatedComboBox = oEvent.getSource();
var sSelectedKey = oValidatedComboBox.getSelectedKey();
var sValue = oValidatedComboBox.getValue();
if (!sSelectedKey && sValue) {
oValidatedComboBox.setValueState("Error");
oValidatedComboBox.setValueStateText("Please enter a valid country!");
} else {
oValidatedComboBox.setValueState("None");
}
},
Instead of using jquery, use UI5 control's methods and properties:
The sap.m.ComboBox borrows the following two methods from sap.m.InputBase:
setEditable
setEnabled
or since you are using property binding for the visibility, do the same for the editable property, e.g. {Data0200>/editableShovel}

SAPUI5 Table cell valuestate disappear

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.

SAPUI5 formatter function returns its parameter

I have a very strange issue with formatter function.
I have the following formatter:
_formatter: function(sAccessLevel){
switch(sAccessLevel){
case "fullAccess":
return true;
case "readOnly":
return false;
case "norefund":
return false;
case "nodiscount":
return false;
default:
return false;
}
},
for the following field:
<Column visible="{path: 'modelName>/Access', formatter: '_formatter'}" >
<header hAlign="Middle" vAlign="Middle">
<Text text="{i18n>discount}" textAlign="Center"/>
</header>
</Column>
And in try-catch block I sometimes get the following error:
Error: "nodiscount" is of type string, expected boolean for property "visible" of Element sap.m.Column#__column154
or
Error: "fullAccess" is of type string, expected boolean for property "visible" of Element sap.m.Column#__column489
When I try to reproduce the error, the code works fine, but in production system's log I see the above error.
How this error possible?
Thank you.
Its a simple mistake. While giving the formatter in XML view we have to give the .formatterFunctionName as below.
<Column visible="{path: 'modelName>/Access', formatter: '._formatter'}" >
<header hAlign="Middle" vAlign="Middle">
<Text text="{i18n>discount}" textAlign="Center"/>
</header>
</Column>
In the controller implement your formatter function as usual.