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