Conditional formatting in table line - sapui5

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

Related

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?

sapui5 how to display value over 100 percentage on radial chart

I am trying to show percentage value on radial chart, but radial chart's percentage property only allow me to display value between 0 and 100.
How can I do this?
Having a view with id__xmlview0,
and a chart with id RadialMicroChart1:
$("#__xmlview0--RadialMicroChart1 > svg > text").text(yourText)
It will work even if you have several charts.
SAP does not allow values over 100% for Radial Micro Charts, you could try setting the value using JQuery and manipulating fraction and total to fit your case.
Using the charts unique id for the first chart chartRadial1 to get it's element.
Update:
Bare in mind that, depending on the layout of your view, the children might change and you would have to research more into nth and children selectors to make sure you are selecting the right one but for this example its the svg's 6th child.
var percent = $(".sapSuiteRMCFont");
percent.text("200%");
OR In Controller & View:
press: function(oEvent) {
var myVal = 150,
myTotal = 200,
actTotal = 100,
newVal = (myVal * actTotal) / myTotal;
this.getView().byId("chartRadial1").setPercentage(newVal);
//with the given id in xml view, you can be certain only the text for chartRadial1 will change using child selector
setTimeout(function() {
$("div#__xmlview2--chartRadial1 > svg.sapSuiteRMC.sapSuiteRMCSizeResponsive.sapSuiteRMCNeutralTextColor :nth-child(6)").text(myVal + "%");
}, 500);
}
div#__xmlview2--chartRadial1>svg.sapSuiteRMC.sapSuiteRMCSizeResponsive.sapSuiteRMCNeutralTextColor :nth-child(6) {
font-family: fantasy !important;
}
div#__xmlview2--chartRadial2>svg.sapSuiteRMC.sapSuiteRMCSizeResponsive.sapSuiteRMCNeutralTextColor :nth-child(6) {
font-family: arial !important;
}
<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" controllerName="sap.sample.Detail" xmlns:semantic="sap.m.semantic" xmlns:c="sap.suite.ui.microchart">
<content>
<Label text="6.25rem x 6.25rem" width="12.5rem" class="sapUiSmallMargin" />
<FlexBox width="6.25rem" height="6.25rem">
<items>
<c:RadialMicroChart id="chartRadial1" size="Responsive" percentage="99" press="press"></c:RadialMicroChart>
</items>
</FlexBox>
<FlexBox width="6.25rem" height="6.25rem">
<items>
<c:RadialMicroChart id="chartRadial2" size="Responsive" percentage="99" press="press"></c:RadialMicroChart>
</items>
</FlexBox>
</content>
</mvc:View>

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

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.

highlight a changed property on model load

I have a table that where the data is periodically updated by a javascript interval function in my controller:
var model = this.getview().getModel();
var updateModel = setInterval(function(){
model.loadData('path/to/my/data.json');
}, 30000)
This will basically be static display on a public monitor showing a summary of data.
I want to be able to highlight when a property has changed, so I've been trying to add a class to the control when it changes. The class will then highlight this in some way with CSS.
<Table items="{items}">
<columns>
<Column/>
<Column/>
</columns>
<items>
<ColumnListItem>
<cells>
<Text
text="{name}" />
<ObjectStatus
text="{value}"
state="{
path: 'value',
formatter: '.formatter.redOrGreen'
}"/>
</cells>
</ColumnListItem>
</items>
</Table>
So the model updates every 30 seconds. If the {value} field changes, I want to add a class to ObjectStatus control.
At the moment I'm just using a JSON model for local development to see if this is possible, but in production it will be an oData service.
Thanks for the answers, I managed to solve this, but my method wasn't quite covered by the answers on here. This is how I did it:
The requirements for this changed slightly since I posted the question. I'll need to indicate if something has changed, but also if the value has gone up or down. I'll also need to indicate if something goes above or below a certain value. I also wanted to make a solution that could be easily adapted if there are any other future requirements. This will also need to be easily adapted for oData when the backend service is up and running.
First of all (and key to this) is setting up a duplicate model, so this goes into my component.js file .I'm just duplicating the model here so that the values old and new values are unchanged, to make the formatter functions work on the first page load:
var oModel = new JSONModel('/path/to/data.js');
this.setModel(oModel, 'model');
this.setModel(oModel, 'oldModel');
In the controller for my view, I then take a copy of the old data, which goes into the old model that I've attached to the view, the new model is then updated. I do this in the after rendering hook to optimize the initial page load.
onAfterRendering: function(){
var thisView = this.getView();
var updateModel = function(){
var oldData = thisView.getModel('model').getData();
var oldModel = new JSONModel(oldWharehousesData);
thisView.setModel(ollModel, 'oldModel');
//update model
var newModel = thisView.getModel('model');
model.loadData('/path/to/data.js');
};
window.refershInterval = setInterval(updateModel, 30000);
}
I'm then able to input the new and old values to a formatter in my XML view and output a couple of custom data attribute:
<core:CustomData
key="alert-status"
value="{
parts: [
'model>Path/To/My/Property',
'oldModel>Path/To/My/Property'
],
formatter: '.formatter.alertStatus'
}"
writeToDom="true"/>
</customData>
My formatter.js :
alertStatus: function(newValue, oldValue){
var alertNum = 25;
if(newValue < alertNum && oldValue >= alertNum) {
return 'red';
} else if (newValue >= alertNum && oldValue < alertNum) {
return 'green';
} else {
return 'none';
}
}
I can then have as many custom data attributes as I like, run them through their own formatter function, which can be styled to my heart's content, e.g:
compareValues: function(newValue, oldValue) {
if (newValue > oldValue) {
return 'higher';
} else if (newValue < oldValue){
return 'lower';
} else {
return 'false';
}
}
I have build an example on JSBin.
First you have to get the received data. You can use the
Model.attachRequestCompleted event for that:
this.model = new sap.ui.model.json.JSONModel();
this.model.attachRequestCompleted(this.onDataLoaded, this);
In the event handler onDataLoaded you can retrieve the JavaScript object and compare it to a saved copy. You have to write the flags that indicate changes to the array item itself. (Storing it in a separate model as Marc suggested in his comment would not work because in your aggregation binding you only have the one context to your array item.)
At last you have to save the newData object as this.oldData for the next request.
onDataLoaded:function(){
var newData = this.model.getProperty("/");
if (this.oldData){
//diff. You should customize this to your needs.
for(var i = 0, length = Math.min(newData.items.length, this.oldData.items.length); i< length; i++){
newData.items[i].valueChanged = newData.items[i].value !== this.oldData.items[i].value;
newData.items[i].nameChanged = newData.items[i].name !== this.oldData.items[i].name;
}
}
this.oldData = newData;
this.getView().getModel().setProperty("/",newData);
},
You can then bind the ObjectState state property to the flag(s):
<ObjectStatus
text="{value}"
state="{= ${valueChanged} ? 'Warning':'None' }"/>
If you want to change the background color of the whole row or something like that you can apply Bernard's answer and use the flag(s) in a customData attribute.
You can use the <customData> tag
This allows the insertion of a custom attribute into the HTML produced by the XML to HTML conversion process
In the example below for example I add a custom attribute (my own) - this code generates the following attribute data-colour in a relevant HTML element (a <SPAN> tag) - inspect the relevant element using, say, Chrome.
<customData>
<core:CustomData writeToDom="true" key="colour" value="{vproducts>ListCostColour}" />
</customData>
You are then able to create a style for this attribute in your own style sheet as follows (and reference this in your manifest.json)
[data-colour="red"] {
background-color: #ffd1cc;
}
[data-colour="orange"] {
background-color: rgba(255, 243, 184, 0.64);
}
[data-colour="green"] {`enter code here`
background-color: rgba(204, 255, 198, 0.97);
}