Conditional column value in UI5 table - sapui5

I have a column in table whose value is bound to a property of data model.
text = { modelName>/OrderNo}. How to make it conditional based on a flag? If property from Model isReturnable = true, I want to show text = {modelName>/ReturnNo} else I want to show {OrderNo}. How to built syntax for that?
<table:Column>
<Label class="smartist-table-column-header" text="Qty Returned"/>
<table:template>
<Text text="{ path: 'OrderDetail>OrderNo'}"/>
</table:template>
</table:Column>

You can use expression binding.
See URL for details: https://ui5.sap.com/#/topic/daf6852a04b44d118963968a1239d2c0
Solution to your problem:
<Text text="{= ${modelName>isReturnable} ? ${modelName>/ReturnNo} : ${OrderDetail>OrderNo}}" />

As expression binding would be a more appropriate approach to this problem,
Custom formatting can also be one way to achieve this.
In the view:
<Text text= "{ parts:[
{path: "modelName>isReturnable"},
{path: "modelName>ReturnNo"},
{path: "modelName>OrderNo"},
],
formatter: '.formatOrderNo'
}"/>
In the corresponding controller
formatter: function(isReturnable, sReturnNo, sOrderNo){
if(isReturnable == true){
return sReturnNo;
}else{
return OrderNo;
}
}
In case of more complex logic where you need to perform some calculations/manipulations on the fields before binding, custom formatting is the way to go. Custom Formatters in SAPUI5

Related

SAPUI5 How to display only the first element of an expanded entitySet in table

I am currently using an entityset(i.e, "SolutioningVersions") in my smarttable which i have expanded in controler using
var mBindingParams = oEvent.getParameter("bindingParams");
mBindingParams.parameters["expand"] = "TEAMID/TEAMDETAILS,SOLREQ,SOLESTIMATE";
I have bound it in my table using
<VBox items="{ path: 'SOLESTIMATE', templateShareable:false }">
<Text text="{WBSVARIANT}"/>
</VBox>
But "SOLESTIMATE" is having array of objects & i want to show only "WBSVARIANT" from it 1st object in array.
Currently it shows me like this in single cell of table
enter image description here
I want to show only 1st element of this array. Also, i dont want to filter array here as objects are not unique.
A formatter can help you:
<Text text="{path: 'WBSVARIANT', formatter: '.formatText'}"/>
function formatText(items){
return items[0];
}

Button Filter to Table

Trying to add a filter that takes a table that uses xsodata and have a dropdown that applies a filter
current code:
var testButton = new sap.m.Button('filterTable', {
text: "Filter",
tooltip: "Filter table to selection",
icon: sap.ui.core.IconPool.getIconURI("filter"),
press: new sap.ui.model.Filter(testTable['testColumn'], sap.ui.model.FilterOperator.EQ, "testValue")
});
I think that this example in the SDK is what you are looking for. Take a look at how the "availability" column is declared in the XML view and at the implementation of the toggleAvailabilityFilter function in the controller.
EDIT: Here the basic code, as example.
How the column should be declared in the XML View:
<Column
id="columnId"
filterProperty="Available"
showFilterMenuEntry="false"
defaultFilterOperator="EQ"
filterType="sap.ui.model.type.Boolean">
<m:Label text="Status" />
<template>
...template...
</template>
</Column>
Example of button callback:
toggleAvailabilityFilter : function(oEvent) {
this.byId("columnId").filter(oEvent.getParameter("pressed") ? "X" : "");
},

How to bind a calendar and two time pickers properly to a sap.m.table?

I am learning SAPUI5 at the moment and created a little application for myself to play around and learn by practice. Before I get to my question, I will provide some short information about my app, so you know what I am trying to do and what should be the expected result.
What does the app?
The only thing this application does is providing a calendar and a table for the user. If the user clicks on a date, then the table should be filled with this date and two time pickers to give a start and end time for this selected day. For every date a new row should be created.
The actual problem:
The problem is a design problem I guess. The table gets filled with the dates and the time pickers, but my way of doing this is, is bad practice I guess. I store my selected dates in two models, one for the calendar, one copy to bind it to the table. That works and dates are saved and I can access the data via model. When a new dates get added to the table, the two timepickers get added too because they are provided in the ColumnListItem in the XML view, but no model is bound to them nor do I have access to the values of the timepickers e.g. via ID. And here is the problem, until now I found no proper way of how to them bind to a model or another clean way nor to access the values of the created timepickers.
The question:
How would you implement this in a clean way, so that the time pickers are bound in a right way maybe to a model or models? and you can access their data? I would be thankful if you could give me an advice or hint how I should implement this in a clean way since I want to learn from this and don't want to start hacking around with bad practices just to achieve the goal in shorter time.
The relevant sourcecode:
Controller:
var CalendarController = Controller.extend("sap.ui.unified.sample.CalendarMultipleDaySelection.CalendarMultipleDaySelection", {
oFormatYyyymmdd: null,
oModel: null,
onInit: function(oEvt) {
this.oFormatYyyymmdd = sap.ui.core.format.DateFormat.getInstance({
pattern: "dd.MM.yyyy",
calendarType: sap.ui.core.CalendarType.Gregorian
});
this.oModel = new JSONModel({
selectedDates: []
});
this.oCopyModel = new JSONModel({
selectedDates: []
});
var oCalendar = this.getView().byId("calendar");
oCalendar.setModel(this.oModel);
},
handleCalendarSelect: function(oEvt) {
var oCalendar = oEvt.oSource;
var aSelectedDates = oCalendar.getSelectedDates();
console.log(aSelectedDates);
var oDate;
var oData = {
selectedDates: []
};
var oTable = this.getView().byId("dateTable");
if (aSelectedDates.length > 0) {
for (var i = 0; i < aSelectedDates.length; i++) {
oDate = aSelectedDates[i].getStartDate();
oData.selectedDates.push({
Date: this.oFormatYyyymmdd.format(oDate)
});
}
this.oModel.setData(oData);
if (this.oCopyModel.getProperty("/selectedDates/length") >= 0) {
this.oCopyModel.setData(oData);
oTable.setModel(this.oCopyModel);
}
} else {
this._clearModel();
}
},
return CalendarController;
View:
<content>
<unified:Calendar id="calendar" select="handleCalendarSelect" intervalSelection="false" singleSelection="false"/>
<Table id="dateTable" items="{path: '/selectedDates', sorter: {path: 'Date', comparator: '.dateComperator'}}"mode="None" fixedLayout="true">
<columns>
<Column>
<header>
<Text text="Date"/>
</header>
</Column>
<Column>
<header>
<Text text="Beginning"/>
</header>
</Column>
<Column>
<header>
<Text text="End"/>
</header>
</Column>
</columns>
<ColumnListItem>
<Text text="{Date}"/>
<TimePicker value="10:00" valueFormat="HH:mm" displayFormat="HH:mm" change="handleChange"/>
<TimePicker value="11:00" valueFormat="HH:mm" displayFormat="HH:mm" change="handleChange"/>
</ColumnListItem>
</Table>
Kind regards
Maximilian
I created a small example:
https://next.plnkr.co/edit/OGmJimjF2YZ46mv6DsF2?preview
A few points:
I simply added a few properties (startTime and endTime) to a selected date. You can now modify the time with the timepicker, the changes are stored in the model.
The data binding of the calender seems broken. I also had to use getSelectedDates. This may be due to singleSelection="false". When using single selection you can access the selected date (or interval) via data binding.
Never access internal properties (oEvt.oSource). There are accessors for this (oEvt.getSource()).

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();

Change format of sap.m.Text component from the view

I can quite handily change a button in the view using it's property 'type' in an expression..
type="{= (${Orders>SupplierNote} && ${Orders>SupplierNote} !== '') ?
'Reject' : (${Orders>InternalNote} && ${Orders>InternalNote} !== '') ?
'Emphasized' : 'Default'}"/>
The problem is, how do I do this for a Text component?
I can't overwrite the class and it doesn't have a type.
Here's how I implemented CustomData to add a class with expression Binding...
In the View....
<Text text="{Orders>EmailAddress}" tooltip="{Orders>EmailAddress}">
<customData>
<core:CustomData key="mydata" value="{= (${Orders>Status} === '2' ) ? 'Red' : (${Orders>Status} === '1') ? 'Green' : (${Orders>Status} === '0') ? 'Amber' : ''}" writeToDom="true" />
</customData>
</Text>
Now the CSS.....
.sapMText[data-mydata="Red"] {
color:#cc1919;
}
.sapMText[data-mydata="Green"] {
color:#007833;
}
.sapMText[data-mydata="Amber"] {
color:#d14900;
}
It's quit understandable that the Text control does not have something like the type property. In the context of a Button it has a clear semantic (Accept, Reject, ...) while it would be hard to achieve the same for a Text control. However, the type of a Button is just used by the renderer to apply a specific style class. You can do something similar with a Text as well:
<Text class="customStyleClass" text="Hellow World!"/>
Now your custom style class is applied. Unfortunately expression binding does not work here. If you need to make the style dependent on your data you can write custom data to DOM and use it in your custom style class. However, this should be used sparsely.
You could use two text controls, each carrying one of your class options, then use conditional control on the visible attribute.
<Text class="customStyleClass_1" text="Hellow World!" visible="{= ${somevalue} > '0' ? false : true }"/>
<Text class="customStyleClass_2" text="Hellow World!" visible="{= ${somevalue} > '0' ? true : false }"/>
There are weaknesses in this approach, for example the overhead if there are many such text controls, and also if you have to refer to the text on code then you would need to determine the visible version etc.